/* * DynamORM - Dynamic Object-Relational Mapping library. * Copyright (c) 2012-2026, Grzegorz Russek (grzegorz.russek@gmail.com) * All rights reserved. */ using DynamORM.Builders.Extensions; using DynamORM.Builders.Implementation; using DynamORM.Builders; using DynamORM.Helpers.Dynamics; using DynamORM.Helpers; using DynamORM.Mapper; using DynamORM.Objects; using DynamORM.TypedSql; using DynamORM.Validation; using System.Collections.Concurrent; using System.Collections.Generic; using System.Collections.Specialized; using System.Collections; using System.ComponentModel; using System.Data.Common; using System.Data; using System.Dynamic; using System.IO; using System.Linq.Expressions; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Serialization; using System.Text.RegularExpressions; using System.Text; using System; [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 class Data { internal DataTable _schema; internal int _fields; internal int _rows; internal int _rowsAffected; internal int _position = -1; internal int _cachePos = -1; internal IList _names; internal IDictionary _ordinals; internal IList _types; internal IList _cache; } private List _data = new List(); private int _currentDataPosition = 0; private bool _isDisposed; 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 dynamic enumerable. /// List of objects. /// Instance of containing objects data. public static DynamicCachedReader FromDynamicEnumerable(IEnumerable objects) { var first = (objects as IEnumerable).FirstOrDefault(); if (first == null) return null; var firstDict = first as IDictionary; var r = new DynamicCachedReader(); r.Init(firstDict.Keys.Count); r._data.Add(new Data()); var data = r._data[r._currentDataPosition]; for (int i = 0; i < firstDict.Keys.Count; i++) data._types.Add(null); foreach (dynamic elem in (objects as IEnumerable)) { int c = 0; var dict = elem as IDictionary; foreach (var k in firstDict.Keys) { object val = dict[k]; data._cache.Add(val); if (data._types[c] == null && val != null) data._types[c] = val.GetType(); c++; } data._rows++; } for (int i = 0; i < firstDict.Keys.Count; i++) if (data._types[i] == null) data._types[i] = typeof(string); data._schema = new DataTable("DYNAMIC"); data._schema.Columns.Add(new DataColumn("ColumnName", typeof(string))); data._schema.Columns.Add(new DataColumn("ColumnOrdinal", typeof(int))); data._schema.Columns.Add(new DataColumn("ColumnSize", typeof(int))); data._schema.Columns.Add(new DataColumn("NumericPrecision", typeof(short))); data._schema.Columns.Add(new DataColumn("NumericScale", typeof(short))); data._schema.Columns.Add(new DataColumn("DataType", typeof(Type))); data._schema.Columns.Add(new DataColumn("ProviderType", typeof(int))); data._schema.Columns.Add(new DataColumn("NativeType", typeof(int))); data._schema.Columns.Add(new DataColumn("AllowDBNull", typeof(bool))); data._schema.Columns.Add(new DataColumn("IsUnique", typeof(bool))); data._schema.Columns.Add(new DataColumn("IsKey", typeof(bool))); data._schema.Columns.Add(new DataColumn("IsAutoIncrement", typeof(bool))); int ordinal = 0; DataRow dr = null; foreach (var column in firstDict.Keys) { dr = data._schema.NewRow(); dr[0] = column; dr[1] = ordinal; dr[2] = 0; dr[3] = 0; dr[4] = 0; dr[5] = data._types[ordinal]; dr[6] = data._types[ordinal].ToDbType(); dr[7] = data._types[ordinal].ToDbType(); dr[8] = true; dr[9] = false; dr[10] = false; dr[11] = false; data._schema.Rows.Add(dr); data._names.Add(dr[0].ToString()); data._ordinals.Add(dr[0].ToString().ToUpper(), ordinal++); data._types.Add((Type)dr[5]); dr.AcceptChanges(); } dr.AcceptChanges(); return r; } /// 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; 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; return r; } private void InitDataReader(IDataReader reader, int offset = 0, int limit = -1, Func progress = null) { do { Init(reader.FieldCount); var data = _data[_currentDataPosition]; data._schema = reader.GetSchemaTable(); data._rowsAffected = reader.RecordsAffected; int i = 0; for (i = 0; i < data._fields; i++) { data._names.Add(reader.GetName(i)); data._types.Add(reader.GetFieldType(i)); if (!data._ordinals.ContainsKey(reader.GetName(i).ToUpper())) data._ordinals.Add(reader.GetName(i).ToUpper(), i); } int current = 0; while (reader.Read()) { if (current < offset) { current++; continue; } for (i = 0; i < data._fields; i++) data._cache.Add(reader[i]); data._rows++; current++; if (limit >= 0 && data._rows >= limit) break; if (progress != null && !progress(this, data._rows)) break; } IsClosed = false; data._position = -1; data._cachePos = -1; if (progress != null) progress(this, data._rows); } while (reader.NextResult()); reader.Close(); } private void FillFromEnumerable(IEnumerable objects, DynamicTypeMap mapper) { var data = _data[_currentDataPosition]; foreach (var elem in objects) { foreach (var col in mapper.ColumnsMap) { object val = null; if (col.Value.Get != null) val = col.Value.Get(elem); data._cache.Add(val); } data._cache.Add(elem); data._rows++; } } private void FillFromEnumerable(Type elementType, IEnumerable objects, DynamicTypeMap mapper) { var data = _data[_currentDataPosition]; foreach (var elem in objects) { foreach (var col in mapper.ColumnsMap) { object val = null; if (col.Value.Get != null) val = col.Value.Get(elem); data._cache.Add(val); } data._cache.Add(elem); data._rows++; } } private void CreateSchemaTable(DynamicTypeMap mapper) { var data = _data[_currentDataPosition]; data._schema = new DataTable("DYNAMIC"); data._schema.Columns.Add(new DataColumn("ColumnName", typeof(string))); data._schema.Columns.Add(new DataColumn("ColumnOrdinal", typeof(int))); data._schema.Columns.Add(new DataColumn("ColumnSize", typeof(int))); data._schema.Columns.Add(new DataColumn("NumericPrecision", typeof(short))); data._schema.Columns.Add(new DataColumn("NumericScale", typeof(short))); data._schema.Columns.Add(new DataColumn("DataType", typeof(Type))); data._schema.Columns.Add(new DataColumn("ProviderType", typeof(int))); data._schema.Columns.Add(new DataColumn("NativeType", typeof(int))); data._schema.Columns.Add(new DataColumn("AllowDBNull", typeof(bool))); data._schema.Columns.Add(new DataColumn("IsUnique", typeof(bool))); data._schema.Columns.Add(new DataColumn("IsKey", typeof(bool))); data._schema.Columns.Add(new DataColumn("IsAutoIncrement", typeof(bool))); int ordinal = 0; DataRow dr = null; foreach (var column in mapper.ColumnsMap) { dr = data._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; data._schema.Rows.Add(dr); data._names.Add(dr[0].ToString()); data._ordinals.Add(dr[0].ToString().ToUpper(), ordinal++); data._types.Add((Type)dr[5]); dr.AcceptChanges(); } dr = data._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; data._schema.Rows.Add(dr); data._names.Add("#O"); data._ordinals.Add("#O".ToUpper(), ordinal++); data._types.Add(mapper.Type); dr.AcceptChanges(); } private void Init(int fieldCount) { _data.Add(new Data() { _rows = 0, _fields = fieldCount, _names = new List(fieldCount), _ordinals = new Dictionary(fieldCount), _types = new List(fieldCount), _cache = new List(fieldCount * 100), }); _currentDataPosition = _data.Count - 1; } /// Sets the current position in reader. /// The position. public void SetPosition(int pos) { if (pos >= -1 && pos < _data[_currentDataPosition]._rows) { _data[_currentDataPosition]._position = pos; _data[_currentDataPosition]._cachePos = _data[_currentDataPosition]._position * _data[_currentDataPosition]._fields; } else throw new IndexOutOfRangeException(); } #endregion Helpers #region IDataReader Members /// Closes the System.Data.IDataReader Object. public void Close() { IsClosed = true; _currentDataPosition = -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 _data[_currentDataPosition]._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 _data[_currentDataPosition]._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() { _currentDataPosition++; return _data.Count < _currentDataPosition; } /// Advances the System.Data.IDataReader to the next record. /// Returns true if there are more rows; otherwise, false. public bool Read() { var data = _data[_currentDataPosition]; data._cachePos = (++data._position) * data._fields; return data._position < data._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 { return _data[_currentDataPosition]._rowsAffected; } } #endregion IDataReader Members #region IDisposable Members /// Performs application-defined tasks associated with /// freeing, releasing, or resetting unmanaged resources. public void Dispose() { if (_isDisposed) return; _isDisposed = true; IsClosed = true; if (_data == null) return; foreach (var data in _data) { if (data == null) continue; if (data._names != null) data._names.Clear(); if (data._types != null) data._types.Clear(); if (data._cache != null) data._cache.Clear(); if (data._schema != null) data._schema.Dispose(); } _data.Clear(); } #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 _data[_currentDataPosition]._fields; } } /// Return the value of the specified field. /// The index of the field to find. /// Field value upon return. public bool GetBoolean(int i) { var data = _data[_currentDataPosition]; return (bool)data._cache[data._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) { var data = _data[_currentDataPosition]; return (byte)data._cache[data._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) { var data = _data[_currentDataPosition]; using (MemoryStream ms = new MemoryStream((byte[])data._cache[data._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) { var data = _data[_currentDataPosition]; return (char)data._cache[data._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) { var data = _data[_currentDataPosition]; using (MemoryStream ms = new MemoryStream((byte[])data._cache[data._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 _data[_currentDataPosition]._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) { var data = _data[_currentDataPosition]; return (DateTime)data._cache[data._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) { var data = _data[_currentDataPosition]; return (decimal)data._cache[data._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) { var data = _data[_currentDataPosition]; return (double)data._cache[data._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 _data[_currentDataPosition]._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) { var data = _data[_currentDataPosition]; return (float)data._cache[data._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) { var data = _data[_currentDataPosition]; return (Guid)data._cache[data._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) { var data = _data[_currentDataPosition]; return (short)data._cache[data._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) { var data = _data[_currentDataPosition]; return (int)data._cache[data._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) { var data = _data[_currentDataPosition]; return (long)data._cache[data._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 _data[_currentDataPosition]._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) { var data = _data[_currentDataPosition]; if (data._ordinals.ContainsKey(name.ToUpper())) return data._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) { var data = _data[_currentDataPosition]; return (string)data._cache[data._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) { var data = _data[_currentDataPosition]; return data._cache[data._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) { var data = _data[_currentDataPosition]; for (int i = 0; i < data._fields; i++) values[i] = data._cache[data._cachePos + i]; return data._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) { var data = _data[_currentDataPosition]; return data._cache[data._cachePos + i] == null || data._cache[data._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 { var data = _data[_currentDataPosition]; if (data._ordinals.ContainsKey(name.ToUpper())) return data._cache[data._cachePos + data._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 { var data = _data[_currentDataPosition]; return data._cache[data._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() { ParameterDirection = ParameterDirection.Input; } /// 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 parameter direction when used in procedure invocation. public ParameterDirection ParameterDirection { 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() { if (IsDisposed) return; var db = _db; if (db == null) { IsDisposed = true; return; } lock (db.SyncLock) { if (IsDisposed) return; IsDisposed = true; if (_con != null) { List pool = db.CommandsPool.TryGetValue(_con.Connection); if (pool != null && pool.Contains(this)) pool.Remove(this); } if (_command != null) { _command.Parameters.Clear(); _command.Dispose(); _command = null; } _con = null; _db = 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() { if (IsDisposed) return; var db = _db; if (db != null && Connection != null) db.Close(Connection); Connection = null; _db = null; 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. /// /// /// using (var db = GetORM()) /// { /// db.Execute(@"CREATE OR ALTER PROCEDURE sp_Exp_Scalar AS SELECT 42;"); /// var res0 = db.Procedures.sp_Exp_Scalar(); /// var res1 = db.Procedures.sp_Exp_Scalar(); /// /// db.Execute(@"CREATE OR ALTER PROCEDURE sp_Exp_ReturnInt AS RETURN 42;"); /// var res2 = db.Procedures.sp_Exp_ReturnInt(); /// /// db.Execute(@"CREATE OR ALTER PROCEDURE sp_Exp_SomeData AS /// SELECT 1 Id, 'Some Name 1' [Name], 'Some Desc 1' [Desc], GETDATE() [Date] /// UNION ALL SELECT 2 Id, 'Some Name 2', 'Some Desc 2', GETDATE() [Date];"); /// var res3 = db.Procedures.sp_Exp_SomeData(); /// var res4 = db.Procedures.sp_Exp_SomeData>(); /// /// db.Execute(@"CREATE OR ALTER PROCEDURE sp_Exp_SomeInputAndOutput /// @Name nvarchar(50), /// @Result nvarchar(256) OUTPUT /// AS /// SELECT @Result = 'Hi, ' + @Name + ' your lucky number is 42!';"); /// var res5 = db.Procedures.sp_Exp_SomeInputAndOutput(Name: "G4g4r1n", out_Result: new DynamicColumn /// { /// Schema = new DynamicSchemaColumn /// { /// Size = 256, /// }, /// }, ret_Return: 0); /// var res6 = db.Procedures.sp_Exp_SomeInputAndOutput(Name: "G4g4r1n", out_Result: new DynamicSchemaColumn /// { /// Size = 256, /// }, ret_Return: 0); /// /// db.Execute(@"CREATE OR ALTER PROCEDURE sp_Exp_SomeInputAndOutputWithDataAndReturn /// @Name nvarchar(50), /// @Result nvarchar(256) OUTPUT /// AS /// SELECT @Result = 'Hi, ' + @Name + ' your lucky number is 42!' /// /// SELECT 1 Id, 'Some Name 1' [Name], 'Some Desc 1' [Desc], GETDATE() [Date] /// UNION ALL SELECT 2 Id, 'Some Name 2', 'Some Desc 2', GETDATE() [Date] /// /// RETURN 42;"); /// var res7 = db.Procedures.sp_Exp_SomeInputAndOutputWithDataAndReturn, sp_Exp_SomeInputAndOutputWithDataAndReturn_Result>(Name: "G4g4r1n", out_Result: new DynamicColumn /// { /// Schema = new DynamicSchemaColumn /// { /// Size = 256, /// }, /// }, ret_Return: 0); /// var res8 = db.Procedures.sp_Exp_SomeInputAndOutputWithDataAndReturn, sp_Exp_SomeInputAndOutputWithDataAndReturn_Result>(Name: "G4g4r1n", out_Result: new DynamicSchemaColumn /// { /// Size = 256, /// }, ret_Return: 0); /// } /// ///private class sp_Exp_SomeData_Result ///{ /// public virtual int Id { get; set; } /// public virtual string Name { get; set; } /// public virtual string Desc { get; set; } /// public virtual DateTime Date { get; set; } ///} ///private class sp_Exp_SomeInputAndOutput_Result ///{ /// public virtual string Result { get; set; } /// public virtual string Return { get; set; } ///} ///private class sp_Exp_SomeInputAndOutputWithDataAndReturn_Result ///{ /// public class Data /// { /// public virtual int Id { get; set; } /// public virtual string Name { get; set; } /// public virtual string Desc { get; set; } /// public virtual DateTime Date { get; set; } /// } /// public virtual List sp_Exp_SomeInputAndOutputWithDataAndReturn { get; set; } /// public virtual string Result { get; set; } /// public virtual string Return { get; set; } ///} /// /// public dynamic Procedures { get { if (_proc == null) { if ((Options & DynamicDatabaseOptions.SupportStoredProcedures) != DynamicDatabaseOptions.SupportStoredProcedures) throw new InvalidOperationException("Database connection doesn'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; } #if NETFRAMEWORK // https://github.com/dotnet/runtime/issues/26229 /// 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) { } #endif /// 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); } private DbProviderFactory FindDbProviderFactoryFromConnection(Type t) { foreach (var type in t.Assembly.GetTypes().Where(x => x.IsSubclassOf(typeof(DbProviderFactory)))) { DbProviderFactory provider = null; bool dispose = false; var pi = type.GetProperty("Instance", BindingFlags.Static | BindingFlags.Public | BindingFlags.GetProperty); if (pi != null) provider = (DbProviderFactory)pi.GetValue(null, null); else { var fi = type.GetField("Instance", BindingFlags.Static | BindingFlags.Public | BindingFlags.GetField); if (fi != null) provider = (DbProviderFactory)fi.GetValue(null); } if (provider == null) { var ci = type.GetConstructor(Type.EmptyTypes); if (ci != null) { provider = ci.Invoke(null) as DbProviderFactory; dispose = true; } } try { if (provider != null) { using (var c = provider.CreateConnection()) { if (c.GetType() == t) return provider; } } } finally { if (provider != null && dispose && provider is IDisposable) ((IDisposable)provider).Dispose(); } } return null; } /// 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 _provider = FindDbProviderFactoryFromConnection(connection.GetType()); 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. /// Table alias. /// use no lock. /// This instance to permit chaining. public virtual IDynamicSelectQueryBuilder From(string alias = null, bool noLock = false) { // TODO: Make it more readable and maitainable if (noLock) { if (string.IsNullOrEmpty(alias)) return new DynamicSelectQueryBuilder(this).From(x => x(typeof(T)).NoLock()); else return new DynamicSelectQueryBuilder(this).From(x => x(typeof(T)).As(alias).NoLock()); } else { if (string.IsNullOrEmpty(alias)) return new DynamicSelectQueryBuilder(this).From(x => x(typeof(T))); else return new DynamicSelectQueryBuilder(this).From(x => x(typeof(T)).As(alias)); } } /// Adds to the FROM clause using . /// Type which can be represented in database. /// Table alias. /// use no lock. /// This instance to permit chaining. public virtual IDynamicTypedSelectQueryBuilder FromTyped(string alias = null, bool noLock = false) { // TODO: Make it more readable and maitainable DynamicTypedSelectQueryBuilder builder = new DynamicTypedSelectQueryBuilder(this); if (noLock) { if (string.IsNullOrEmpty(alias)) builder.From(x => x(typeof(T)).NoLock()); else builder.From(x => x(typeof(T)).As(alias).NoLock()); } else { if (string.IsNullOrEmpty(alias)) builder.From(x => x(typeof(T))); else builder.From(x => x(typeof(T)).As(alias)); } return builder; } /// Adds to the FROM clause using a typed scope builder with evolving join arity. /// Type which can be represented in database. /// Table alias. /// use no lock. /// Scope builder instance. public virtual IDynamicTypedSelectScopeQueryBuilder FromTypedScope(string alias = null, bool noLock = false) { DynamicTypedSelectQueryBuilder builder = (DynamicTypedSelectQueryBuilder)FromTyped(alias, noLock); string resolvedAlias = string.IsNullOrEmpty(alias) ? builder.Tables[0].Alias ?? builder.Tables[0].Name : alias; return new DynamicTypedSelectScopeQueryBuilder(builder, resolvedAlias); } /// 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 IDynamicTypedInsertQueryBuilder InsertTyped() { return new DynamicTypedInsertQueryBuilder(this, true); } /// 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 IDynamicTypedUpdateQueryBuilder UpdateTyped() { return new DynamicTypedUpdateQueryBuilder(this, true); } /// 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 IDynamicTypedDeleteQueryBuilder DeleteTyped() { return new DynamicTypedDeleteQueryBuilder(this, true); } /// 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) { using (IDbConnection con = Open()) using (IDbCommand cmd = con.CreateCommand()) { using (IDataReader rdr = cmd .SetCommand(sql) .AddParameters(this, args) .ExecuteReader()) using (IDataReader 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) { using (IDbConnection con = Open()) using (IDbCommand cmd = con.CreateCommand()) { using (IDataReader rdr = cmd .SetCommand(builder) .ExecuteReader()) using (var 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 CachedQuery /// 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 DynamicCachedReader CachedQuery(string sql, params object[] args) { using (IDbConnection con = Open()) using (IDbCommand cmd = con.CreateCommand()) { using (IDataReader rdr = cmd .SetCommand(sql) .AddParameters(this, args) .ExecuteReader()) return new DynamicCachedReader(rdr); } } /// Enumerate the reader and yield the result. /// Command builder. /// Enumerator of objects expanded from query. public virtual DynamicCachedReader CachedQuery(IDynamicQueryBuilder builder) { using (IDbConnection con = Open()) using (IDbCommand cmd = con.CreateCommand()) { using (IDataReader rdr = cmd .SetCommand(builder) .ExecuteReader()) return new DynamicCachedReader(rdr); } } #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. public 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().Name == "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.ToLower()) .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 (IsDisposed) return; #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(); _proc = null; _tempConn = null; 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, /// Database support stored procedures results (Return value). SupportStoredProceduresResult = 0x00000200, /// Database support with no lock syntax. SupportNoLock = 0x00001000, /// 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(TimeSpan), DbType.Time }, { 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(TimeSpan?), DbType.Time }, { 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 = CorrectValue(p.DbType, ((IDictionary)item).Values.FirstOrDefault()); else p.Value = CorrectValue(p.DbType, 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 = CorrectValue(p.DbType, 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 = CorrectValue(p.DbType, value); } cmd.Parameters.Add(p); return cmd; } private static object CorrectValue(DbType type, object value) { if (value == null || value == DBNull.Value) return DBNull.Value; if ((type == DbType.String || type == DbType.AnsiString || type == DbType.StringFixedLength || type == DbType.AnsiStringFixedLength) && !(value is string)) return value.ToString(); else if (type == DbType.Guid && value is string) return Guid.Parse(value.ToString()); else if (type == DbType.Guid && value is byte[] && ((byte[])value).Length == 16) return new Guid((byte[])value); else if (type == DbType.DateTime && value is TimeSpan) // HACK: This is specific for SQL Server, to be verified with other databases return DateTime.Today.Add((TimeSpan)value); return value; } /// 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 : CorrectValue(p.DbType, 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 = CorrectValue(p.DbType, 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 = CorrectValue(param.DbType, 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 = CorrectValue(param.DbType, 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 = CorrectValue(param.DbType, 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 = CorrectValue(param.DbType, 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 = CorrectValue(param.DbType, 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 = CorrectValue(param.DbType, 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 { var p = ((IDbDataParameter)command.Parameters[parameterName]); p.Value = CorrectValue(p.DbType, 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 { var p = ((IDbDataParameter)command.Parameters[index]); p.Value = CorrectValue(p.DbType, 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) { 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 true if type is anonymous. public static bool IsAnonymous(this Type type) { // HACK: The only way to detect anonymous types right now. return Attribute.IsDefined(type, typeof(CompilerGeneratedAttribute), false) && type.IsGenericType && (type.Name.Contains("AnonymousType") || type.Name.Contains("AnonType")) && (type.Name.StartsWith("<>") || type.Name.StartsWith("VB$")) && (type.Attributes & TypeAttributes.NotPublic) == TypeAttributes.NotPublic; } /// Check if type implements IEnumerable<> interface. /// Type to check. /// Returns true if it does. public static bool IsGenericEnumerable(this Type type) { return type.IsGenericType && type.GetInterfaces().Any(t => t.IsGenericType && t.GetGenericTypeDefinition() == typeof(IEnumerable<>)); } /// Check if type implements IEnumerable<> interface. /// Type to check. /// Returns true if it does. public static bool IsNullableType(this Type type) { Type generic = type.IsGenericType ? type.GetGenericTypeDefinition() : null; if (generic != null && generic.Equals(typeof(Nullable<>)) && (type.IsClass || type.IsValueType || type.IsEnum)) return true; return false; } /// Check if type is collection of any kind. /// Type to check. /// Returns true if it is. public static bool IsCollection(this Type type) { if (!type.IsArray) return type.IsGenericEnumerable(); return true; } /// Check if type is collection of value types like int. /// Type to check. /// Returns true if it is. public static bool IsCollectionOfValueTypes(this Type type) { if (type.IsArray) return type.GetElementType().IsValueType; else { if (type.IsGenericType && type.GetInterfaces().Any(t => t.GetGenericTypeDefinition() == typeof(IEnumerable<>))) { Type[] gt = type.GetGenericArguments(); return (gt.Length == 1) && gt[0].IsValueType; } } return false; } /// Gets corresponding to the /// provided . /// The type to be converted. /// Returns corresponding to the /// provided . public static DbType ToDbType(this Type t) { return TypeMap.TryGetNullable(t) ?? DbType.Object; } /// Gets corresponding to the /// provided . /// The type to be converted. /// Returns corresponding to the /// provided . public static Type ToType(this DbType dbt) { if (dbt == DbType.String) return typeof(string); foreach (KeyValuePair tdbt in TypeMap) if (tdbt.Value == dbt) return tdbt.Key; return typeof(object); } /// Determines whether the specified value is has only ASCII chars. /// The value to check. /// Returns true if the specified value has only ASCII cars; otherwise, false. public static bool IsASCII(this string value) { return Encoding.UTF8.GetByteCount(value) == value.Length; } #endregion Type extensions #region IDictionary extensions /// Gets the value associated with the specified key. /// The type of keys in the dictionary. /// The type of values in the dictionary. /// Dictionary to probe. /// The key whose value to get. /// Nullable type containing value or null if key was not found. public static TValue? TryGetNullable(this IDictionary dict, TKey key) where TValue : struct { TValue val; if (key != null && dict.TryGetValue(key, out val)) return val; return null; } /// Gets the value associated with the specified key. /// The type of keys in the dictionary. /// The type of values in the dictionary. /// Dictionary to probe. /// The key whose value to get. /// Instance of object or null if not found. public static TValue TryGetValue(this IDictionary dict, TKey key) where TValue : class { TValue val; if (key != null && dict != null && dict.TryGetValue(key, out val)) return val; return default(TValue); } /// Adds element to dictionary and returns added value. /// The type of keys in the dictionary. /// The type of values in the dictionary. /// Dictionary to which add value. /// The key under which value value will be added. /// Value to add. /// Instance of object or null if not found. public static TValue AddAndPassValue(this IDictionary dict, TKey key, TValue value) { dict.Add(key, value); return value; } #endregion IDictionary extensions #region IDataReader extensions /// Gets the information corresponding /// to the type of that would be returned from /// . /// The data reader. /// The index of the field to find. /// The information corresponding to the /// type of that would be returned from /// . public static DbType GetFieldDbType(this IDataReader r, int i) { return TypeMap.TryGetNullable(r.GetFieldType(i)) ?? DbType.String; } internal static IEnumerable EnumerateReader(this IDataReader r) { while (r.Read()) yield return r.RowToDynamic(); } /// Creates cached reader object from non cached reader. /// The reader to cache. /// The offset row. /// The limit to number of tows. -1 is no limit. /// The progress delegate. /// Returns new instance of cached reader or current instance of a reader. public static IDataReader CachedReader(this IDataReader r, int offset = 0, int limit = -1, Func progress = null) { if (r is DynamicCachedReader) return r; return new DynamicCachedReader(r, offset, limit, progress); } #endregion IDataReader extensions #region Mapper extensions /// MapEnumerable object enumerator into specified type. /// Type to which columnMap results. /// Source enumerator. /// Enumerator of specified type. public static IEnumerable MapEnumerable(this IEnumerable enumerable) { DynamicTypeMap mapper = DynamicMapperCache.GetMapper(); if (mapper == null) throw new InvalidOperationException("Type can't be mapped for unknown reason."); foreach (object item in enumerable) yield return (T)mapper.Create(item); } /// MapEnumerable object item into specified type. /// Type to which columnMap results. /// Source object. /// Item of specified type. public static T Map(this object item) { DynamicTypeMap mapper = DynamicMapperCache.GetMapper(); if (mapper == null) throw new InvalidOperationException("Type can't be mapped for unknown reason."); return (T)mapper.Create(item); } /// MapEnumerable object enumerator into specified type using property names. /// Type to which columnMap results. /// Source enumerator. /// Enumerator of specified type. public static IEnumerable MapEnumerableByProperty(this IEnumerable enumerable) { DynamicTypeMap mapper = DynamicMapperCache.GetMapper(); if (mapper == null) throw new InvalidOperationException("Type can't be mapped for unknown reason."); foreach (object item in enumerable) yield return (T)mapper.CreateByProperty(item); } /// MapEnumerable object item into specified type using property names. /// Type to which columnMap results. /// Source object. /// Item of specified type. public static T MapByProperty(this object item) { DynamicTypeMap mapper = DynamicMapperCache.GetMapper(); if (mapper == null) throw new InvalidOperationException("Type can't be mapped for unknown reason."); return (T)mapper.CreateByProperty(item); } /// Fill object of specified type with data from source object. /// Type to which columnMap results. /// Item to which columnMap data. /// Item from which extract data. /// Filled item. public static T Fill(this T item, object source) { DynamicTypeMap mapper = DynamicMapperCache.GetMapper(); if (mapper == null) throw new InvalidOperationException("Type can't be mapped for unknown reason."); mapper.Map(source, item); return item; } /// Fill object of specified type with data from source object using property names. /// Type to which columnMap results. /// Item to which columnMap data. /// Item from which extract data. /// Filled item. public static T FillByProperty(this T item, object source) { DynamicTypeMap mapper = DynamicMapperCache.GetMapper(); if (mapper == null) throw new InvalidOperationException("Type can't be mapped for unknown reason."); mapper.MapByProperty(source, item); return item; } /// MapEnumerable object enumerator into specified type. /// Source enumerator. /// Type to which columnMap results. /// Enumerator of specified type. public static IEnumerable MapEnumerable(this IEnumerable enumerable, Type type) { DynamicTypeMap mapper = DynamicMapperCache.GetMapper(type); if (mapper == null) throw new InvalidOperationException("Type can't be mapped for unknown reason."); foreach (object item in enumerable) yield return mapper.Create(item); } /// MapEnumerable object item into specified type. /// Source object. /// Type to which columnMap results. /// Item of specified type. public static object Map(this object item, Type type) { DynamicTypeMap mapper = DynamicMapperCache.GetMapper(type); if (mapper == null) throw new InvalidOperationException("Type can't be mapped for unknown reason."); return mapper.Create(item); } /// MapEnumerable object enumerator into specified type using property names. /// Source enumerator. /// Type to which columnMap results. /// Enumerator of specified type. public static IEnumerable MapEnumerableByProperty(this IEnumerable enumerable, Type type) { DynamicTypeMap mapper = DynamicMapperCache.GetMapper(type); if (mapper == null) throw new InvalidOperationException("Type can't be mapped for unknown reason."); foreach (object item in enumerable) yield return mapper.CreateByProperty(item); } /// MapEnumerable object item into specified type using property names. /// Source object. /// Type to which columnMap results. /// Item of specified type. public static object MapByProperty(this object item, Type type) { DynamicTypeMap mapper = DynamicMapperCache.GetMapper(type); if (mapper == null) throw new InvalidOperationException("Type can't be mapped for unknown reason."); return mapper.CreateByProperty(item); } /// Converts the elements of an /// to the specified type. /// The type to convert the elements of source to. /// The that /// contains the elements to be converted. /// An enumerator that contains each element of /// the source sequence converted to the specified type. /// Thrown when /// source is null. /// An element in the /// sequence cannot be cast to type T or enumerator /// is not . public static IEnumerable CastEnumerable(this object enumerator) { return (enumerator as System.Collections.IEnumerable).Cast(); } #endregion Mapper extensions #region TryParse extensions #if !DYNAMORM_OMMIT_TRYPARSE /// Generic try parse. /// Type to parse to. /// Value to parse. /// Handler of a try parse method. /// Returns true if conversion was successful. public static T? TryParse(this string value, TryParseHandler handler) where T : struct { if (String.IsNullOrEmpty(value)) return null; T result; if (handler(value, out result)) return result; return null; } /// Generic try parse with default value. /// Type to parse to. /// Value to parse. /// Default value of a result. /// Handler of a try parse method. /// Returns true if conversion was successful. public static T TryParseDefault(this string value, T defaultValue, TryParseHandler handler) { if (String.IsNullOrEmpty(value)) return defaultValue; T result; if (handler(value, out result)) return result; return defaultValue; } /// Delegate from try parse function of a type. /// Type which implements this function. /// Value to parse. /// Resulting value. /// Returns true if conversion was successful. public delegate bool TryParseHandler(string value, out T result); #endif #endregion TryParse extensions #region Coalesce - besicaly not an extensions /// Select first not null value. /// Type to return. /// Values to check. /// First not null or default value. public static T Coalesce(params T[] vals) where T : class { return vals.FirstOrDefault(v => v != null); } /// Select first not null value. /// Type to return. /// Values to check. /// First not null or default value. public static T? CoalesceNullable(params T?[] vals) where T : struct { return vals.FirstOrDefault(v => v != null); } #endregion Coalesce - besicaly not an extensions } /// Dynamic procedure invoker. /// Unfortunately I can use out and ref to /// return parameters, . /// But see example for workaround. If there aren't any return parameters execution will return scalar value. /// Scalar result is not converted to provided generic type (if any). For output results there is possibility to map to provided class. /// You still can use out, return and both way parameters by providing variable prefix: /// dynamic res = db.Procedures.sp_Test_Scalar_In_Out(inp: Guid.NewGuid(), out_outp: Guid.Empty); /// Console.Out.WriteLine(res.outp); /// Prefixes: out_, ret_, both_. Result will contain field without prefix. /// Here is an example with result class: /// public class ProcResult { [Column("outp")] public Guid Output { get; set; } } /// ProcResult res4 = db.Procedures.sp_Test_Scalar_In_Out<ProcResult>(inp: Guid.NewGuid(), out_outp: Guid.Empty) as ProcResult; /// As you can se, you can use mapper to do job for you. public class DynamicProcedureInvoker : DynamicObject, IDisposable { private DynamicDatabase _db; private List _prefixes; private bool _isDisposed; internal DynamicProcedureInvoker(DynamicDatabase db, List prefixes = null) { _prefixes = prefixes; _db = db; } /// This is where the magic begins. /// Binder to create owner. /// Binder invoke result. /// Returns true if invoke was performed. public override bool TryGetMember(GetMemberBinder binder, out object result) { List pref = new List(); if (_prefixes != null) pref.AddRange(_prefixes); pref.Add(binder.Name); result = new DynamicProcedureInvoker(_db, pref); return true; } /// This is where the magic begins. /// Binder to invoke. /// Binder arguments. /// Binder invoke result. /// Returns true if invoke was performed. public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result) { // parse the method CallInfo info = binder.CallInfo; // Get generic types IList types = binder.GetGenericTypeArguments() ?? new List(); Type declaredResultType = null; Dictionary retParams = null; using (IDbConnection con = _db.Open()) using (IDbCommand cmd = con.CreateCommand()) { if (_prefixes == null || _prefixes.Count == 0) cmd.SetCommand(CommandType.StoredProcedure, binder.Name); else cmd.SetCommand(CommandType.StoredProcedure, string.Format("{0}.{1}", string.Join(".", _prefixes), binder.Name)); #region Prepare arguments int alen = args.Length; bool retIsAdded = false; declaredResultType = alen == 1 ? DynamicProcedureResultBinder.GetDeclaredResultType(args[0]) : null; if (alen > 0) { if (alen == 1 && DynamicProcedureParameterBinder.CanBind(args[0])) { DynamicProcedureParameterBinder.BindingResult bindingResult = DynamicProcedureParameterBinder.Bind(_db, cmd, args[0]); retParams = bindingResult.ReturnParameters; retIsAdded = bindingResult.ReturnValueAdded; } else { for (int i = 0; i < alen; i++) { object arg = args[i]; if (arg is DynamicExpando) cmd.AddParameters(_db, (DynamicExpando)arg); else if (arg is ExpandoObject) cmd.AddParameters(_db, (ExpandoObject)arg); else if (arg is DynamicColumn) { var dcv = (DynamicColumn)arg; string paramName = i.ToString(); bool isOut = false; bool isRet = false; bool isBoth = false; if (info.ArgumentNames.Count > i) { isOut = info.ArgumentNames[i].StartsWith("out_"); isRet = info.ArgumentNames[i].StartsWith("ret_"); isBoth = info.ArgumentNames[i].StartsWith("both_"); paramName = isOut || isRet ? info.ArgumentNames[i].Substring(4) : isBoth ? info.ArgumentNames[i].Substring(5) : info.ArgumentNames[i]; } paramName = dcv.Alias ?? dcv.ColumnName ?? (dcv.Schema.HasValue ? dcv.Schema.Value.Name : null) ?? paramName; if (!isOut && !isRet && !isBoth) { isOut = dcv.ParameterDirection == ParameterDirection.Output; isRet = dcv.ParameterDirection == ParameterDirection.ReturnValue; isBoth = dcv.ParameterDirection == ParameterDirection.InputOutput; } if (isRet) retIsAdded = true; if (isOut || isRet || isBoth) { if (retParams == null) retParams = new Dictionary(); retParams.Add(paramName, cmd.Parameters.Count); } if (dcv.Schema != null) { var ds = dcv.Schema.Value; cmd.AddParameter( _db.GetParameterName(paramName), isOut ? ParameterDirection.Output : isRet ? ParameterDirection.ReturnValue : isBoth ? ParameterDirection.InputOutput : ParameterDirection.Input, ds.Type, ds.Size, ds.Precision, ds.Scale, (isOut || isRet) ? DBNull.Value : dcv.Value); } else cmd.AddParameter( _db.GetParameterName(paramName), isOut ? ParameterDirection.Output : isRet ? ParameterDirection.ReturnValue : isBoth ? ParameterDirection.InputOutput : ParameterDirection.Input, arg == null ? DbType.String : arg.GetType().ToDbType(), isRet ? 4 : 0, (isOut || isRet) ? DBNull.Value : dcv.Value); } else if (arg is DynamicSchemaColumn) { var dsc = (DynamicSchemaColumn)arg; string paramName = i.ToString(); bool isOut = false; bool isRet = false; bool isBoth = false; if (info.ArgumentNames.Count > i) { isOut = info.ArgumentNames[i].StartsWith("out_"); isRet = info.ArgumentNames[i].StartsWith("ret_"); isBoth = info.ArgumentNames[i].StartsWith("both_"); paramName = isOut || isRet ? info.ArgumentNames[i].Substring(4) : isBoth ? info.ArgumentNames[i].Substring(5) : info.ArgumentNames[i]; } paramName = dsc.Name ?? paramName; if (isRet) retIsAdded = true; if (isOut || isRet || isBoth) { if (retParams == null) retParams = new Dictionary(); retParams.Add(paramName, cmd.Parameters.Count); } cmd.AddParameter( _db.GetParameterName(paramName), isOut ? ParameterDirection.Output : isRet ? ParameterDirection.ReturnValue : isBoth ? ParameterDirection.InputOutput : ParameterDirection.Input, dsc.Type, dsc.Size, dsc.Precision, dsc.Scale, DBNull.Value); } else { if (info.ArgumentNames.Count > i && !string.IsNullOrEmpty(info.ArgumentNames[i])) { bool isOut = info.ArgumentNames[i].StartsWith("out_"); bool isRet = info.ArgumentNames[i].StartsWith("ret_"); bool isBoth = info.ArgumentNames[i].StartsWith("both_"); if (isRet) retIsAdded = true; string paramName = isOut || isRet ? info.ArgumentNames[i].Substring(4) : isBoth ? info.ArgumentNames[i].Substring(5) : info.ArgumentNames[i]; if (isOut || isBoth || isRet) { if (retParams == null) retParams = new Dictionary(); retParams.Add(paramName, cmd.Parameters.Count); } cmd.AddParameter( _db.GetParameterName(paramName), isOut ? ParameterDirection.Output : isRet ? ParameterDirection.ReturnValue : isBoth ? ParameterDirection.InputOutput : ParameterDirection.Input, arg == null ? isRet ? DbType.Int32 : DbType.String : arg.GetType().ToDbType(), isRet ? 4 : 0, (isOut || isRet) ? DBNull.Value : arg); } else cmd.AddParameter(_db, arg); } } } } #endregion Prepare arguments #region Get main result object mainResult = null; if (types.Count == 0 && DynamicProcedureResultBinder.CanReadResults(declaredResultType)) { using (IDataReader rdr = cmd.ExecuteReader()) using (IDataReader cache = rdr.CachedReader()) mainResult = DynamicProcedureResultBinder.ReadDeclaredResult(declaredResultType, cache); } else if (types.Count > 0) { mainResult = types[0].GetDefaultValue(); if (types[0] == typeof(IDataReader)) { using (IDataReader rdr = cmd.ExecuteReader()) mainResult = rdr.CachedReader(); } else if (types[0] == typeof(DataTable)) { using (IDataReader rdr = cmd.ExecuteReader()) using (IDataReader cache = rdr.CachedReader()) mainResult = cache.ToDataTable(binder.Name); } else if (types[0].IsGenericEnumerable()) { Type argType = types[0].GetGenericArguments().First(); if (argType == typeof(object)) { using (IDataReader rdr = cmd.ExecuteReader()) using (IDataReader cache = rdr.CachedReader()) mainResult = cache.EnumerateReader().ToList(); } else if (argType.IsValueType || argType == typeof(string)) { Type listType = typeof(List<>).MakeGenericType(new Type[] { argType }); IList listInstance = (IList)Activator.CreateInstance(listType); object defVal = listType.GetDefaultValue(); using (IDataReader rdr = cmd.ExecuteReader()) using (IDataReader cache = rdr.CachedReader()) while (cache.Read()) listInstance.Add(cache[0] != null && cache[0] != DBNull.Value ? argType.CastObject(cache[0]) : defVal); mainResult = listInstance; } else if (argType == typeof(Guid)) { Type listType = typeof(List<>).MakeGenericType(new Type[] { argType }); IList listInstance = (IList)Activator.CreateInstance(listType); using (IDataReader rdr = cmd.ExecuteReader()) using (IDataReader cache = rdr.CachedReader()) while (cache.Read()) { if (cache[0] != null && cache[0] != DBNull.Value && Guid.TryParse(cache[0].ToString(), out Guid g)) listInstance.Add(g); } mainResult = listInstance; } else { DynamicTypeMap mapper = DynamicMapperCache.GetMapper(argType); if (mapper == null) throw new InvalidCastException(string.Format("Don't know what to do with this type: '{0}'.", argType.ToString())); using (IDataReader rdr = cmd.ExecuteReader()) using (IDataReader cache = rdr.CachedReader()) { var lt = typeof(List<>); var ltc = lt.MakeGenericType(argType); var instance = Activator.CreateInstance(ltc) as IList; foreach (var item in cache.EnumerateReader()) instance.Add(DynamicExtensions.Map(item, argType)); mainResult = instance; } //mainResult = cache.EnumerateReader().MapEnumerable(argType).ToList(); } } else if (types[0].IsValueType || types[0] == typeof(string)) { mainResult = cmd.ExecuteScalar(); if (mainResult != null && mainResult != DBNull.Value) mainResult = types[0].CastObject(mainResult); } else if (types[0] == typeof(Guid)) { mainResult = cmd.ExecuteScalar(); if (mainResult != null && mainResult != DBNull.Value && Guid.TryParse(mainResult.ToString(), out Guid g)) mainResult = g; } else { DynamicTypeMap mapper = DynamicMapperCache.GetMapper(types[0]); if (mapper == null) throw new InvalidCastException(string.Format("Don't know what to do with this type: '{0}'.", types[0].ToString())); using (IDataReader rdr = cmd.ExecuteReader()) if (rdr.Read()) mainResult = (rdr.RowToDynamic() as object).Map(types[0]); else mainResult = null; } } else { var returnName = _db.GetParameterName("___result___"); if ((_db.Options & DynamicDatabaseOptions.SupportStoredProceduresResult) == DynamicDatabaseOptions.SupportStoredProceduresResult) { if (!retIsAdded) cmd.AddParameter(returnName, ParameterDirection.ReturnValue, DbType.Int32, 4, 0, 0, DBNull.Value); } mainResult = cmd.ExecuteNonQuery(); IDbDataParameter returnParam = null; if (!retIsAdded) { if ((_db.Options & DynamicDatabaseOptions.SupportStoredProceduresResult) == DynamicDatabaseOptions.SupportStoredProceduresResult) returnParam = cmd.Parameters[returnName] as IDbDataParameter; } else { foreach (var e in cmd.Parameters) { var p = e as IDbDataParameter; if (p != null && p.Direction == ParameterDirection.ReturnValue) { returnParam = p; break; } } } if (returnParam != null && returnParam.Value != null && returnParam.Value != DBNull.Value) mainResult = returnParam.Value; } #endregion Get main result #region Handle out params if (retParams != null) { Dictionary res = new Dictionary(); if (mainResult != null) { if (mainResult == DBNull.Value) res.Add(binder.Name, null); else res.Add(binder.Name, mainResult); } foreach (KeyValuePair pos in retParams) res.Add(pos.Key, ((IDbDataParameter)cmd.Parameters[pos.Value]).Value); if (types.Count > 1) { DynamicTypeMap mapper = DynamicMapperCache.GetMapper(types[1]); if (mapper != null) result = mapper.Create(res.ToDynamic()); else result = res.ToDynamic(); } else if (declaredResultType != null) result = DynamicProcedureResultBinder.BindPayload(declaredResultType, binder.Name, mainResult, res.Where(x => x.Key != binder.Name).ToDictionary(x => x.Key, x => x.Value), mainResult != null && declaredResultType.IsInstanceOfType(mainResult) ? mainResult : null); else result = res.ToDynamic(); } else if (declaredResultType != null && mainResult != null && declaredResultType.IsInstanceOfType(mainResult)) result = mainResult; else if (declaredResultType != null) result = DynamicProcedureResultBinder.BindPayload(declaredResultType, binder.Name, mainResult, null); else result = mainResult; #endregion Handle out params } return true; } /// Performs application-defined tasks associated with /// freeing, releasing, or resetting unmanaged resources. public void Dispose() { if (_isDisposed) return; _isDisposed = true; _db = null; _prefixes = null; } } /// Dynamic query exception. [Serializable] public class DynamicQueryException : Exception, ISerializable { /// Initializes a new instance of the /// class. /// The command which failed. public DynamicQueryException(IDbCommand command = null) : base(string.Format("Error executing command.{0}{1}", Environment.NewLine, command != null ? command.DumpToString() : string.Empty)) { } /// Initializes a new instance of the /// class. /// The message. /// The command which failed. public DynamicQueryException(string message, IDbCommand command = null) : base(string.Format("{0}{1}{2}", message, Environment.NewLine, command != null ? command.DumpToString() : string.Empty)) { } /// Initializes a new instance of the /// class. /// The inner exception. /// The command which failed. public DynamicQueryException(Exception innerException, IDbCommand command = null) : base(string.Format("Error executing command.{0}{1}", Environment.NewLine, command != null ? command.DumpToString() : string.Empty), innerException) { } /// Initializes a new instance of the /// class. /// The message. /// The inner exception. /// The command which failed. public DynamicQueryException(string message, Exception innerException, IDbCommand command = null) : base(string.Format("{0}{1}{2}", message, Environment.NewLine, command != null ? command.DumpToString() : string.Empty), innerException) { } } /// Stores information about column from database schema. public struct DynamicSchemaColumn { /// Gets or sets column name. public string Name { get; set; } /// Gets or sets column type. public DbType Type { get; set; } /// Gets or sets a value indicating whether column is a key. public bool IsKey { get; set; } /// Gets or sets a value indicating whether column should have unique value. public bool IsUnique { get; set; } /// Gets or sets a value indicating whether column allows null or not. public bool AllowNull { get; set; } /// Gets or sets column size. public int Size { get; set; } /// Gets or sets column precision. public byte Precision { get; set; } /// Gets or sets column scale. public byte Scale { get; set; } } #if !DYNAMORM_OMMIT_OLDSYNTAX /// Dynamic table is a simple ORM using dynamic objects. /// /// Assume that we have a table representing Users class. /// /// Let's take a look at Query possibilities. Assume we want /// to get enumerator for all records in database, mapped to our class /// instead of dynamic type we can use following syntax. /// Approach first. Use dynamic Query method and just set type /// then just cast it to user class. Remember that you must cast result /// of Queryto IEnumerable<object>. because from /// point of view of runtime you are operating on object type. /// (db.Table<User>().Query(type: typeof(User)) as IEnumerable<object>).Cast<User>(); /// Second approach is similar. We ask database using dynamic /// Query method. The difference is that we use extension method of /// IEnumerable<object> (to which we must cast to) to map /// object. /// (db.Table<User>().Query(columns: "*") as IEnumerable<object>).MapEnumerable<User>(); /// You can also use generic approach. But be careful this method is currently available thanks to framework hack. /// (db.Table<User>().Query<User>() as IEnumerable<object>).Cast<User>() /// Another approach uses existing methods, but still requires a /// cast, because Query also returns dynamic object enumerator. /// (db.Table<User>().Query().Execute() as IEnumerable<object>).MapEnumerable<User>(); /// /// Below you can find various invocations of dynamic and non dynamic /// methods of this class. x variable is a class instance. /// First various selects: /// x.Count(columns: "id"); /// x.Count(last: new DynamicColumn /// { /// Operator = DynamicColumn.CompareOperator.In, /// Value = new object[] { "Hendricks", "Goodwin", "Freeman" }.Take(3) /// }); /// x.Count(last: new DynamicColumn /// { /// Operator = DynamicColumn.CompareOperator.In, /// Value = new object[] { "Hendricks", "Goodwin", "Freeman" } /// }); /// x.First(columns: "id").id; /// x.Last(columns: "id").id; /// x.Count(first: "Ori"); /// x.Min(columns: "id"); /// x.Max(columns: "id"); /// x.Avg(columns: "id"); /// x.Sum(columns: "id"); /// x.Scalar(columns: "first", id: 19); /// x.Scalar(columns: "first:first:group_concat", id: new DynamicColumn { Operator = DynamicColumn.CompareOperator.Lt, Value = 20 }); /// x.Scalar(columns: "group_concat(first):first", id: new DynamicColumn { Operator = DynamicColumn.CompareOperator.Lt, Value = 20 }); /// var v = (x.Query(columns: "first,first:occurs:count", group: "first", order: ":desc:2") as IEnumerable<dynamic>).ToList(); /// x.Scalar(columns: @"length(""login""):len:avg"); /// x.Avg(columns: @"length(""email""):len"); /// x.Count(condition1: /// new DynamicColumn() /// { /// ColumnName = "email", /// Aggregate = "length", /// Operator = DynamicColumn.CompareOperator.Gt, /// Value = 27 /// }); /// var o = x.Single(columns: "id,first,last", id: 19); /// x.Single(where: new DynamicColumn("id").Eq(100)).login; /// x.Count(where: new DynamicColumn("id").Not(100)); /// x.Single(where: new DynamicColumn("login").Like("Hoyt.%")).id; /// x.Count(where: new DynamicColumn("login").NotLike("Hoyt.%")); /// x.Count(where: new DynamicColumn("id").Greater(100)); /// x.Count(where: new DynamicColumn("id").GreaterOrEqual(100)); /// x.Count(where: new DynamicColumn("id").Less(100)); /// x.Count(where: new DynamicColumn("id").LessOrEqual(100)); /// x.Count(where: new DynamicColumn("id").Between(75, 100)); /// x.Count(where: new DynamicColumn("id").In(75, 99, 100)); /// x.Count(where: new DynamicColumn("id").In(new[] { 75, 99, 100 })); /// Inserts: /// x.Insert(code: 201, first: "Juri", last: "Gagarin", email: "juri.gagarin@megacorp.com", quote: "bla, bla, bla"); /// x.Insert(values: new { code = 202, first = "Juri", last = "Gagarin", email = "juri.gagarin@megacorp.com", quote = "bla, bla, bla" }); /// x.Insert(values: new Users /// { /// Id = u.Max(columns: "id") + 1, /// Code = "203", /// First = "Juri", /// Last = "Gagarin", /// Email = "juri.gagarin@megacorp.com", /// Quote = "bla, bla, bla" /// }); /// x.Insert(values: new users /// { /// id = u.Max(columns: "id") + 1, /// code = "204", /// first = "Juri", /// last = "Gagarin", /// email = "juri.gagarin@megacorp.com", /// quote = "bla, bla, bla" /// }); /// x.Update(id: 1, code: 201, first: "Juri", last: "Gagarin", email: "juri.gagarin@megacorp.com", quote: "bla, bla, bla"); /// x.Update(update: new { id = 2, code = 202, first = "Juri", last = "Gagarin", email = "juri.gagarin@megacorp.com", quote = "bla, bla, bla" }); /// Updates: /// x.Update(update: new Users /// { /// Id = 3, /// Code = "203", /// First = "Juri", /// Last = "Gagarin", /// Email = "juri.gagarin@megacorp.com", /// Quote = "bla, bla, bla" /// }); /// x.Update(update: new users /// { /// id = 4, /// code = "204", /// first = "Juri", /// last = "Gagarin", /// email = "juri.gagarin@megacorp.com", /// quote = "bla, bla, bla" /// }); /// x.Update(values: new { code = 205, first = "Juri", last = "Gagarin", email = "juri.gagarin@megacorp.com", quote = "bla, bla, bla" }, where: new { id = 5 }); /// x.Update(values: new Users /// { /// Id = 6, /// Code = "206", /// First = "Juri", /// Last = "Gagarin", /// Email = "juri.gagarin@megacorp.com", /// Quote = "bla, bla, bla" /// }, id: 6); /// x.Update(values: new users /// { /// id = 7, /// code = "207", /// first = "Juri", /// last = "Gagarin", /// email = "juri.gagarin@megacorp.com", /// quote = "bla, bla, bla" /// }, id: 7); /// Delete: /// x.Delete(code: 10); /// x.Delete(delete: new { id = 11, code = 11, first = "Juri", last = "Gagarin", email = "juri.gagarin@megacorp.com", quote = "bla, bla, bla" }); /// x.Delete(delete: new Users /// { /// Id = 12, /// Code = "12", /// First = "Juri", /// Last = "Gagarin", /// Email = "juri.gagarin@megacorp.com", /// Quote = "bla, bla, bla" /// }); /// x.Delete(delete: new users /// { /// id = 13, /// code = "13", /// first = "Juri", /// last = "Gagarin", /// email = "juri.gagarin@megacorp.com", /// quote = "bla, bla, bla" /// }); /// x.Delete(where: new { id = 14, code = 14 }); /// public class DynamicTable : DynamicObject, IExtendedDisposable, ICloneable { private static HashSet _allowedCommands = new HashSet { "Insert", "Update", "Delete", "Query", "Single", "Where", "First", "Last", "Get", "Count", "Sum", "Avg", "Min", "Max", "Scalar" }; /// Gets dynamic database. internal DynamicDatabase Database { get; private set; } /// Gets type of table (for coning and schema building). internal Type TableType { get; private set; } /// Gets name of table. public virtual string TableName { get; private set; } /// Gets name of owner. public virtual string OwnerName { get; private set; } /// Gets full name of table containing owner and table name. public virtual string FullName { get { return string.IsNullOrEmpty(TableName) ? null : string.IsNullOrEmpty(OwnerName) ? Database.DecorateName(TableName) : string.Format("{0}.{1}", Database.DecorateName(OwnerName), Database.DecorateName(TableName)); } } /// Gets table schema. /// If database doesn't support schema, only key columns are listed here. public virtual Dictionary Schema { get; private set; } private DynamicTable() { } /// Initializes a new instance of the class. /// Database and connection management. /// Table name. /// Owner of the table. /// Override keys in schema. public DynamicTable(DynamicDatabase database, string table = "", string owner = "", string[] keys = null) { IsDisposed = false; Database = database; TableName = Database.StripName(table); OwnerName = owner != null ? Database.StripName(owner) : string.Empty; TableType = null; BuildAndCacheSchema(keys); } /// Initializes a new instance of the class. /// Database and connection management. /// Type describing table. /// Override keys in schema. public DynamicTable(DynamicDatabase database, Type type, string[] keys = null) { if (type == null) throw new ArgumentNullException("type", "Type can't be null."); IsDisposed = false; Database = database; TableType = type; DynamicTypeMap mapper = DynamicMapperCache.GetMapper(type); if (mapper != null) { TableName = mapper.Table == null || string.IsNullOrEmpty(mapper.Table.Name) ? type.Name : mapper.Table.Name; OwnerName = mapper.Table == null || string.IsNullOrEmpty(mapper.Table.Owner) ? null : mapper.Table.Owner; } BuildAndCacheSchema(keys); } #region Schema private void BuildAndCacheSchema(string[] keys) { Dictionary schema = null; schema = Database.GetSchema(TableType) ?? Database.GetSchema(TableName, OwnerName); #region Fill currrent table schema if (keys == null && TableType != null) { DynamicTypeMap mapper = DynamicMapperCache.GetMapper(TableType); if (mapper != null) { IEnumerable k = mapper.ColumnsMap.Where(p => p.Value.Column != null && p.Value.Column.IsKey).Select(p => p.Key); if (k.Count() > 0) keys = k.ToArray(); } } if (schema != null) { if (keys == null) Schema = new Dictionary(schema); else { // TODO: Make this.... nicer List ks = keys.Select(k => k.ToLower()).ToList(); Schema = schema.ToDictionary(k => k.Key, (v) => { DynamicSchemaColumn dsc = v.Value; dsc.IsKey = ks.Contains(v.Key); return dsc; }); } } #endregion Fill currrent table schema #region Build ad-hock schema if (keys != null && Schema == null) { Schema = keys.Select(k => k.ToLower()).ToList() .ToDictionary(k => k, k => new DynamicSchemaColumn { Name = k, IsKey = true }); } #endregion Build ad-hock schema } #endregion Schema #region Basic Queries /// Enumerate the reader and yield the result. /// SQL query containing numbered parameters in format provided by /// methods. Also names should be formatted with /// method. /// Arguments (parameters). /// Enumerator of objects expanded from query. public virtual IEnumerable Query(string sql, params object[] args) { return Database.Query(sql, args); } /// Enumerate the reader and yield the result. /// Command builder. /// Enumerator of objects expanded from query. public virtual IEnumerable Query(IDynamicQueryBuilder builder) { return Database.Query(builder); } /// Create new . /// New instance. public virtual IDynamicSelectQueryBuilder Query() { IDynamicSelectQueryBuilder builder = new DynamicSelectQueryBuilder(this.Database); string name = this.FullName; if (!string.IsNullOrEmpty(name)) builder.From(x => name); return builder; } /// Returns a single result. /// SQL query containing numbered parameters in format provided by /// methods. Also names should be formatted with /// method. /// Arguments (parameters). /// Result of a query. public virtual object Scalar(string sql, params object[] args) { return Database.Scalar(sql, args); } /// Returns a single result. /// Command builder. /// Result of a query. public virtual object Scalar(IDynamicQueryBuilder builder) { return Database.Scalar(builder); } #if !DYNAMORM_OMMIT_GENERICEXECUTION && !DYNAMORM_OMMIT_TRYPARSE /// Returns a single result. /// What kind of result is expected. /// SQL query containing numbered parameters in format provided by /// methods. Also names should be formatted with /// method. /// Arguments (parameters). /// Result of a query. public virtual T ScalarAs(string sql, params object[] args) { return Database.ScalarAs(sql, args); } /// Returns a single result. /// What kind of result is expected. /// Command builder. /// Default value. /// Result of a query. public virtual T ScalarAs(IDynamicQueryBuilder builder, T defaultValue = default(T)) { return Database.ScalarAs(builder, defaultValue); } #endif /// Execute stored procedure. /// Name of stored procedure to execute. /// Number of affected rows. public virtual int Procedure(string procName) { return Database.Procedure(procName); } /// Execute stored procedure. /// Name of stored procedure to execute. /// Arguments (parameters) in form of expando object. /// Number of affected rows. public virtual int Procedure(string procName, params object[] args) { return Database.Procedure(procName, args); } /// Execute stored procedure. /// Name of stored procedure to execute. /// Arguments (parameters) in form of expando object. /// Number of affected rows. public virtual int Procedure(string procName, DynamicExpando args) { return Database.Procedure(procName, args); } /// Execute stored procedure. /// Name of stored procedure to execute. /// Arguments (parameters) in form of expando object. /// Number of affected rows. public virtual int Procedure(string procName, ExpandoObject args) { return Database.Procedure(procName, args); } /// Execute non query. /// SQL query containing numbered parameters in format provided by /// methods. Also names should be formatted with /// method. /// Arguments (parameters). /// Number of affected rows. public virtual int Execute(string sql, params object[] args) { return Database.Execute(sql, args); } /// Execute non query. /// Command builder. /// Number of affected rows. public virtual int Execute(IDynamicQueryBuilder builder) { return Database.Execute(builder); } /// Execute non query. /// Command builders. /// Number of affected rows. public virtual int Execute(IDynamicQueryBuilder[] builders) { return Database.Execute(builders); } #endregion Basic Queries #region Insert /// Create new . /// New instance. public dynamic Insert() { return new DynamicProxy(new DynamicInsertQueryBuilder(this.Database, this.FullName)); } /// Adds a record to the database. You can pass in an Anonymous object, an , /// A regular old POCO, or a NameValueCollection from a Request.Form or Request.QueryString. /// Anonymous object, an , a regular old POCO, or a NameValueCollection /// from a Request.Form or Request.QueryString, containing fields to update. /// Number of updated rows. public virtual int Insert(object o) { return Insert() .Insert(o) .Execute(); } #endregion Insert #region Update /// Create new . /// New instance. public dynamic Update() { return new DynamicProxy(new DynamicUpdateQueryBuilder(this.Database, this.FullName)); } /// Updates a record in the database. You can pass in an Anonymous object, an ExpandoObject, /// a regular old POCO, or a NameValueCollection from a Request.Form or Request.QueryString. /// Anonymous object, an ExpandoObject, a regular old POCO, or a NameValueCollection /// from a Request.Form or Request.QueryString, containing fields to update. /// Anonymous object, an , a regular old POCO, or a NameValueCollection /// from a Request.Form or Request.QueryString, containing fields with conditions. /// Number of updated rows. public virtual int Update(object o, object key) { return Update() .Values(o) .Where(key) .Execute(); } /// Updates a record in the database using schema. You can pass in an Anonymous object, an ExpandoObject, /// a regular old POCO, or a NameValueCollection from a Request.Form or Request.QueryString. /// Anonymous object, an , a regular old POCO, or a NameValueCollection /// from a Request.Form or Request.QueryString, containing fields to update and conditions. /// Number of updated rows. public virtual int Update(object o) { return Update() .Update(o) .Execute(); } #endregion Update #region Delete /// Create new . /// New instance. public dynamic Delete() { return new DynamicProxy(new DynamicDeleteQueryBuilder(this.Database, this.FullName)); } /// Removes a record from the database. You can pass in an Anonymous object, an , /// A regular old POCO, or a NameValueCollection from a Request.Form or Request.QueryString. /// Anonymous object, an , a regular old POCO, or a NameValueCollection /// from a Request.Form or Request.QueryString, containing fields with where conditions. /// If true use schema to determine key columns and ignore those which /// aren't keys. /// Number of updated rows. public virtual int Delete(object o, bool schema = true) { return Delete() .Where(o, schema) .Execute(); } #endregion Delete #region Universal Dynamic Invoker /// This is where the magic begins. /// Binder to invoke. /// Binder arguments. /// Binder invoke result. /// Returns true if invoke was performed. public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result) { // parse the method CallInfo info = binder.CallInfo; // Get generic types IList types = binder.GetGenericTypeArguments(); // accepting named args only... SKEET! if (info.ArgumentNames.Count != args.Length) throw new InvalidOperationException("Please use named arguments for this type of query - the column name, orderby, columns, etc"); string op = binder.Name; // Avoid strange things if (!_allowedCommands.Contains(op)) throw new InvalidOperationException(string.Format("Dynamic method '{0}' is not supported.", op)); switch (op) { case "Insert": result = DynamicInsert(args, info, types); break; case "Update": result = DynamicUpdate(args, info, types); break; case "Delete": result = DynamicDelete(args, info, types); break; default: result = DynamicQuery(args, info, op, types); break; } return true; } private object DynamicInsert(object[] args, CallInfo info, IList types) { DynamicInsertQueryBuilder builder = new DynamicInsertQueryBuilder(this.Database); if (types != null && types.Count == 1) HandleTypeArgument(null, info, ref types, builder, 0); if (!string.IsNullOrEmpty(this.TableName) && builder.Tables.Count == 0) builder.Table(string.IsNullOrEmpty(this.OwnerName) ? this.TableName : string.Format("{0}.{1}", this.OwnerName, this.TableName), this.Schema); // loop the named args - see if we have order, columns and constraints if (info.ArgumentNames.Count > 0) { for (int i = 0; i < args.Length; i++) { string fullName = info.ArgumentNames[i]; string name = fullName.ToLower(); switch (name) { case "table": if (args[i] is string) builder.Table(args[i].ToString()); else goto default; break; case "values": builder.Insert(args[i]); break; case "type": if (types == null || types.Count == 0) HandleTypeArgument(args, info, ref types, builder, i); else goto default; break; default: builder.Insert(fullName, args[i]); break; } } } // Execute return Execute(builder); } private object DynamicUpdate(object[] args, CallInfo info, IList types) { DynamicUpdateQueryBuilder builder = new DynamicUpdateQueryBuilder(this.Database); if (types != null && types.Count == 1) HandleTypeArgument(null, info, ref types, builder, 0); if (!string.IsNullOrEmpty(this.TableName) && builder.Tables.Count == 0) builder.Table(string.IsNullOrEmpty(this.OwnerName) ? this.TableName : string.Format("{0}.{1}", this.OwnerName, this.TableName), this.Schema); // loop the named args - see if we have order, columns and constraints if (info.ArgumentNames.Count > 0) { for (int i = 0; i < args.Length; i++) { string fullName = info.ArgumentNames[i]; string name = fullName.ToLower(); switch (name) { case "table": if (args[i] is string) builder.Table(args[i].ToString()); else goto default; break; case "update": builder.Update(args[i]); break; case "values": builder.Values(args[i]); break; case "where": builder.Where(args[i]); break; case "type": if (types == null || types.Count == 0) HandleTypeArgument(args, info, ref types, builder, i); else goto default; break; default: builder.Update(fullName, args[i]); break; } } } // Execute return Execute(builder); } private object DynamicDelete(object[] args, CallInfo info, IList types) { DynamicDeleteQueryBuilder builder = new DynamicDeleteQueryBuilder(this.Database); if (types != null && types.Count == 1) HandleTypeArgument(null, info, ref types, builder, 0); if (!string.IsNullOrEmpty(this.TableName) && builder.Tables.Count == 0) builder.Table(string.IsNullOrEmpty(this.OwnerName) ? this.TableName : string.Format("{0}.{1}", this.OwnerName, this.TableName), this.Schema); // loop the named args - see if we have order, columns and constraints if (info.ArgumentNames.Count > 0) { for (int i = 0; i < args.Length; i++) { string fullName = info.ArgumentNames[i]; string name = fullName.ToLower(); switch (name) { case "table": if (args[i] is string) builder.Table(args[i].ToString()); else goto default; break; case "where": builder.Where(args[i], false); break; case "delete": builder.Where(args[i], true); break; case "type": if (types == null || types.Count == 0) HandleTypeArgument(args, info, ref types, builder, i); else goto default; break; default: builder.Where(fullName, args[i]); break; } } } // Execute return Execute(builder); } private object DynamicQuery(object[] args, CallInfo info, string op, IList types) { object result; DynamicSelectQueryBuilder builder = new DynamicSelectQueryBuilder(this.Database); if (types != null && types.Count == 1) HandleTypeArgument(null, info, ref types, builder, 0); if (!string.IsNullOrEmpty(this.TableName) && builder.Tables.Count == 0) builder.From(x => string.IsNullOrEmpty(this.OwnerName) ? this.TableName : string.Format("{0}.{1}", this.OwnerName, this.TableName)); // loop the named args - see if we have order, columns and constraints if (info.ArgumentNames.Count > 0) { for (int i = 0; i < args.Length; i++) { string fullName = info.ArgumentNames[i]; string name = fullName.ToLower(); // TODO: Make this nicer switch (name) { case "order": if (args[i] is string) builder.OrderByColumn(((string)args[i]).Split(',')); else if (args[i] is string[]) builder.OrderByColumn(args[i] as string); else if (args[i] is DynamicColumn[]) builder.OrderByColumn((DynamicColumn[])args[i]); else if (args[i] is DynamicColumn) builder.OrderByColumn((DynamicColumn)args[i]); else goto default; break; case "group": if (args[i] is string) builder.GroupByColumn(((string)args[i]).Split(',')); else if (args[i] is string[]) builder.GroupByColumn(args[i] as string); else if (args[i] is DynamicColumn[]) builder.GroupByColumn((DynamicColumn[])args[i]); else if (args[i] is DynamicColumn) builder.GroupByColumn((DynamicColumn)args[i]); else goto default; break; case "columns": { string agregate = (op == "Sum" || op == "Max" || op == "Min" || op == "Avg" || op == "Count") ? op.ToUpper() : null; if (args[i] is string || args[i] is string[]) builder.SelectColumn((args[i] as String).NullOr(s => s.Split(','), args[i] as String[]) .Select(c => { DynamicColumn col = DynamicColumn.ParseSelectColumn(c); if (string.IsNullOrEmpty(col.Aggregate)) col.Aggregate = agregate; return col; }).ToArray()); else if (args[i] is DynamicColumn || args[i] is DynamicColumn[]) builder.SelectColumn((args[i] as DynamicColumn).NullOr(c => new DynamicColumn[] { c }, args[i] as DynamicColumn[]) .Select(c => { if (string.IsNullOrEmpty(c.Aggregate)) c.Aggregate = agregate; return c; }).ToArray()); else goto default; } break; case "where": builder.Where(args[i]); break; case "table": if (args[i] is string) builder.From(x => args[i].ToString()); else goto default; break; case "type": if (types == null || types.Count == 0) HandleTypeArgument(args, info, ref types, builder, i); else goto default; break; default: builder.Where(fullName, args[i]); break; } } } if (op == "Count" && !builder.HasSelectColumns) { result = Scalar(builder.Select(x => x.Count())); if (result is long) result = (int)(long)result; } else if (op == "Sum" || op == "Max" || op == "Min" || op == "Avg" || op == "Count") { if (!builder.HasSelectColumns) throw new InvalidOperationException("You must select one column to agregate."); result = Scalar(builder); if (op == "Count" && result is long) result = (int)(long)result; else if (result == DBNull.Value) result = null; } else { // build the SQL bool justOne = op == "First" || op == "Last" || op == "Get" || op == "Single"; // Be sure to sort by DESC on selected columns if (justOne && !(op == "Last")) { if ((Database.Options & DynamicDatabaseOptions.SupportLimitOffset) == DynamicDatabaseOptions.SupportLimitOffset) builder.Limit(1); else if ((Database.Options & DynamicDatabaseOptions.SupportTop) == DynamicDatabaseOptions.SupportTop) builder.Top(1); } if (op == "Scalar") { if (!builder.HasSelectColumns) throw new InvalidOperationException("You must select one column in scalar statement."); result = Scalar(builder); } else { if (justOne) { if (op == "Last") result = Query(builder).LastOrDefault(); // Last record fallback else result = Query(builder).FirstOrDefault(); // return a single record } else result = Query(builder); // return lots // MapEnumerable to specified result (still needs to be casted after that) if (types != null) { if (types.Count == 1) result = justOne ? result.Map(types[0]) : ((IEnumerable)result).MapEnumerable(types[0]); // TODO: Dictionaries } } } return result; } private void HandleTypeArgument(object[] args, CallInfo info, ref IList types, T builder, int i) where T : DynamicQueryBuilder { if (args != null) { if (args[i] is Type[]) types = new List((Type[])args[i]); else if (args[i] is Type) types = new List(new Type[] { (Type)args[i] }); } if (types != null && types.Count == 1 && !info.ArgumentNames.Any(a => a.ToLower() == "table")) builder.Table(types[0]); } #endregion Universal Dynamic Invoker #region IExtendedDisposable Members /// Performs application-defined tasks associated with /// freeing, releasing, or resetting unmanaged resources. public void Dispose() { if (IsDisposed) return; // Lose reference but don't kill it. if (Database != null) { Database.RemoveFromCache(this); Database = null; } IsDisposed = true; } /// Gets a value indicating whether this instance is disposed. public bool IsDisposed { get; private set; } #endregion IExtendedDisposable Members #region ICloneable Members /// Creates a new object that is a copy of the current /// instance. /// A new object that is a copy of this instance. public object Clone() { return new DynamicTable() { Database = this.Database, Schema = this.Schema, TableName = this.TableName, TableType = this.TableType }; } #endregion ICloneable Members } #endif /// Helper class to easy manage transaction. public class DynamicTransaction : IDbTransaction, IExtendedDisposable { private DynamicDatabase _db; private DynamicConnection _con; private bool _singleTransaction; private Action _disposed; private bool _isDisposed = false; private bool _isOperational = false; /// Initializes a new instance of the class. /// Database connection manager. /// Active connection. /// Are we using single transaction mode? I so... act correctly. /// One of the values. /// This action is invoked when transaction is disposed. /// Pass custom transaction parameters. internal DynamicTransaction(DynamicDatabase db, DynamicConnection con, bool singleTransaction, IsolationLevel? il, Action disposed, object customParams) { _db = db; _con = con; _singleTransaction = singleTransaction; _disposed = disposed; lock (_db.SyncLock) { if (!_db.TransactionPool.ContainsKey(_con.Connection)) throw new InvalidOperationException("Can't create transaction using disposed connection."); else if (_singleTransaction && _db.TransactionPool[_con.Connection].Count > 0) { _isOperational = false; if (_db.DumpCommands && _db.DumpCommandDelegate != null) _db.DumpCommandDelegate(null, "BEGIN TRAN [NON OPERATIONAL]"); } else { if (customParams != null) { MethodInfo mi = _con.Connection.GetType().GetMethods().Where(m => m.GetParameters().Count() == 1 && m.GetParameters().First().ParameterType == customParams.GetType()).FirstOrDefault(); if (mi != null) { _db.TransactionPool[_con.Connection].Push((IDbTransaction)mi.Invoke(_con.Connection, new object[] { customParams, })); if (_db.DumpCommands && _db.DumpCommandDelegate != null) _db.DumpCommandDelegate(null, "BEGIN TRAN [CUSTOM ARGS]"); } else throw new MissingMethodException(string.Format("Method 'BeginTransaction' accepting parameter of type '{0}' in '{1}' not found.", customParams.GetType().FullName, _con.Connection.GetType().FullName)); } else { _db.TransactionPool[_con.Connection] .Push(il.HasValue ? _con.Connection.BeginTransaction(il.Value) : _con.Connection.BeginTransaction()); if (_db.DumpCommands && _db.DumpCommandDelegate != null) _db.DumpCommandDelegate(null, "BEGIN TRAN"); } _db.PoolStamp = DateTime.Now.Ticks; _isOperational = true; } } } /// Commits the database transaction. public void Commit() { lock (_db.SyncLock) { if (_isOperational) { Stack t = _db.TransactionPool.TryGetValue(_con.Connection); if (t != null && t.Count > 0) { IDbTransaction trans = t.Pop(); _db.PoolStamp = DateTime.Now.Ticks; trans.Commit(); trans.Dispose(); if (_db.DumpCommands && _db.DumpCommandDelegate != null) _db.DumpCommandDelegate(null, "COMMIT"); } _isOperational = false; } else if (!_isDisposed && _db.DumpCommands && _db.DumpCommandDelegate != null) _db.DumpCommandDelegate(null, "COMMIT [NON OPERATIONAL]"); } } /// Rolls back a transaction from a pending state. public void Rollback() { lock (_db.SyncLock) { if (_isOperational) { Stack t = _db.TransactionPool.TryGetValue(_con.Connection); if (t != null && t.Count > 0) { IDbTransaction trans = t.Pop(); _db.PoolStamp = DateTime.Now.Ticks; trans.Rollback(); trans.Dispose(); if (_db.DumpCommands && _db.DumpCommandDelegate != null) _db.DumpCommandDelegate(null, "ROLLBACK"); } _isOperational = false; } else if (!_isDisposed && _db.DumpCommands && _db.DumpCommandDelegate != null) _db.DumpCommandDelegate(null, "ROLLBACK [NON OPERATIONAL]"); } } /// Gets connection object to associate with the transaction. public IDbConnection Connection { get { return _con; } } /// Gets for this transaction. public IsolationLevel IsolationLevel { get; private set; } #region IExtendedDisposable Members /// Performs application-defined tasks associated with /// freeing, releasing, or resetting unmanaged resources. public void Dispose() { if (_isDisposed) return; _isDisposed = true; Rollback(); if (_disposed != null) _disposed(); _disposed = null; _con = null; _db = null; } /// Gets a value indicating whether this instance is disposed. public bool IsDisposed { get { return !_isOperational; } } #endregion IExtendedDisposable Members } namespace Builders { /// Typed join kind used by typed fluent builder APIs. public enum DynamicJoinType { Inner = 0, Join, Left, Right, Full, LeftOuter, RightOuter, FullOuter } /// Dynamic delete query builder interface. /// This interface it publicly available. Implementation should be hidden. public interface IDynamicDeleteQueryBuilder : IDynamicQueryBuilder { /// Execute this builder. /// Result of an execution.. int Execute(); /// /// Adds to the 'Where' clause the contents obtained from parsing the dynamic lambda expression given. The condition /// is parsed to the appropriate syntax, where the specific customs virtual methods supported by the parser are used /// as needed. /// - If several Where() methods are chained their contents are, by default, concatenated with an 'AND' operator. /// - The 'And()' and 'Or()' virtual method can be used to concatenate with an 'OR' or an 'AND' operator, as in: /// 'Where( x => x.Or( condition ) )'. /// /// The specification. /// This instance to permit chaining. IDynamicDeleteQueryBuilder Where(Func func); /// Add where condition. /// Condition column with operator and value. /// Builder instance. IDynamicDeleteQueryBuilder Where(DynamicColumn column); /// Add where condition. /// Condition column. /// Condition operator. /// Condition value. /// Builder instance. IDynamicDeleteQueryBuilder Where(string column, DynamicColumn.CompareOperator op, object value); /// Add where condition. /// Condition column. /// Condition value. /// Builder instance. IDynamicDeleteQueryBuilder Where(string column, object value); /// Add where condition. /// Set conditions as properties and values of an object. /// If true use schema to determine key columns and ignore those which /// aren't keys. /// Builder instance. IDynamicDeleteQueryBuilder Where(object conditions, bool schema = false); } /// Dynamic insert query builder interface. /// This interface it publicly available. Implementation should be hidden. public interface IDynamicInsertQueryBuilder : IDynamicQueryBuilder { /// Execute this builder. /// Result of an execution.. int Execute(); /// /// Specifies the columns to insert using the dynamic lambda expressions given. Each expression correspond to one /// column, and can: /// - Resolve to a string, in this case a '=' must appear in the string. /// - Resolve to a expression with the form: 'x => x.Column = Value'. /// /// The specifications. /// The specifications. /// This instance to permit chaining. IDynamicInsertQueryBuilder Values(Func fn, params Func[] func); /// Add insert fields. /// Insert column. /// Insert value. /// Builder instance. IDynamicInsertQueryBuilder Insert(string column, object value); /// Add insert fields. /// Set insert value as properties and values of an object. /// Builder instance. IDynamicInsertQueryBuilder Insert(object o); } /// Dynamic query builder base interface. /// This interface it publicly available. Implementation should be hidden. public interface IDynamicQueryBuilder : IExtendedDisposable { /// Gets instance. DynamicDatabase Database { get; } /// Gets tables information. IList Tables { get; } /// Gets the tables used in this builder. IDictionary Parameters { get; } /// Gets or sets a value indicating whether add virtual parameters. bool VirtualMode { get; set; } /// Gets a value indicating whether database supports standard schema. bool SupportSchema { get; } /// Fill command with query. /// Command to fill. /// Filled instance of . IDbCommand FillCommand(IDbCommand command); /// /// Generates the text this command will execute against the underlying database. /// /// The text to execute against the underlying database. /// This method must be override by derived classes. string CommandText(); /// Gets or sets the on create temporary parameter actions. /// This is exposed to allow setting schema of column. List> OnCreateTemporaryParameter { get; set; } /// Gets or sets the on create real parameter actions. /// This is exposed to allow modification of parameter. List> OnCreateParameter { get; set; } } /// Dynamic select query builder interface. /// This interface it publicly available. Implementation should be hidden. public interface IDynamicSelectQueryBuilder : IDynamicQueryBuilder ////, IEnumerable { /// Execute this builder. /// Enumerator of objects expanded from query. IEnumerable Execute(); /// Execute this builder and map to given type. /// Type of object to map on. /// Enumerator of objects expanded from query. IEnumerable Execute() where T : class; /// Execute this builder as a data reader. /// Action containing reader. void ExecuteDataReader(Action reader); /// Returns a single result. /// Result of a query. object Scalar(); #if !DYNAMORM_OMMIT_GENERICEXECUTION && !DYNAMORM_OMMIT_TRYPARSE /// Returns a single result. /// Type to parse to. /// Default value. /// Result of a query. T ScalarAs(T defaultValue = default(T)); #endif #region From/Join /// /// Adds to the 'From' clause the contents obtained by parsing the dynamic lambda expressions given. The supported /// formats are: /// - Resolve to a string: 'x => "Table AS Alias', where the alias part is optional. /// - Resolve to an expression: 'x => x.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. /// The specification. /// This instance to permit chaining. IDynamicSelectQueryBuilder From(Func fn, params Func[] func); /// /// Adds to the 'Join' clause the contents obtained by parsing the dynamic lambda expressions given. The supported /// formats are: /// - Resolve to a string: 'x => "Table AS Alias ON Condition', where the alias part is optional. /// - Resolve to an expression: 'x => x.Table.As( x.Alias ).On( condition )', where the alias part is optional. /// - Generic expression: 'x => x( expression ).As( x.Alias ).On( condition )', where the alias part is mandatory. /// In this case the alias is not annotated. /// The expression might be prepended by a method that, in this case, is used to specify the specific join type you /// want to perform, as in: 'x => x.Left()...". Two considerations apply: /// - If a 'false' argument is used when no 'Join' part appears in its name, then no 'Join' suffix is added /// with a space in between. /// - If a 'false' argument is used when a 'Join' part does appear, then no split is performed to separate the /// 'Join' part. /// /// The specification. /// This instance to permit chaining. IDynamicSelectQueryBuilder Join(params Func[] func); #endregion From/Join #region Where /// /// Adds to the 'Where' clause the contents obtained from parsing the dynamic lambda expression given. The condition /// is parsed to the appropriate syntax, where the specific customs virtual methods supported by the parser are used /// as needed. /// - If several Where() methods are chained their contents are, by default, concatenated with an 'AND' operator. /// - The 'And()' and 'Or()' virtual method can be used to concatenate with an 'OR' or an 'AND' operator, as in: /// 'Where( x => x.Or( condition ) )'. /// /// The specification. /// This instance to permit chaining. IDynamicSelectQueryBuilder Where(Func func); /// Add where condition. /// Condition column with operator and value. /// Builder instance. IDynamicSelectQueryBuilder Where(DynamicColumn column); /// Add where condition. /// Condition column. /// Condition operator. /// Condition value. /// Builder instance. IDynamicSelectQueryBuilder Where(string column, DynamicColumn.CompareOperator op, object value); /// Add where condition. /// Condition column. /// Condition value. /// Builder instance. IDynamicSelectQueryBuilder Where(string column, object value); /// Add where condition. /// Set conditions as properties and values of an object. /// If true use schema to determine key columns and ignore those which /// aren't keys. /// Builder instance. IDynamicSelectQueryBuilder Where(object conditions, bool schema = false); #endregion Where #region Select /// /// Adds to the 'Select' clause the contents obtained by parsing the dynamic lambda expressions given. The supported /// formats are: /// - Resolve to a string: 'x => "Table.Column AS Alias', where the alias part is optional. /// - Resolve to an expression: 'x => x.Table.Column.As( x.Alias )', where the alias part is optional. /// - Select all columns from a table: 'x => x.Table.All()'. /// - Generic expression: 'x => x( expression ).As( x.Alias )', where the alias part is mandatory. In this case /// the alias is not annotated. /// /// The specification. /// The specification. /// This instance to permit chaining. IDynamicSelectQueryBuilder Select(Func fn, params Func[] func); /// Add select columns. /// Columns to add to object. /// Builder instance. IDynamicSelectQueryBuilder SelectColumn(params DynamicColumn[] columns); /// Add select columns. /// Columns to add to object. /// Column format consist of Column Name, Alias and /// Aggregate function in this order separated by ':'. /// Builder instance. IDynamicSelectQueryBuilder SelectColumn(params string[] columns); #endregion Select #region GroupBy /// /// Adds to the 'Group By' clause the contents obtained from from parsing the dynamic lambda expression given. /// /// The specification. /// The specification. /// This instance to permit chaining. IDynamicSelectQueryBuilder GroupBy(Func fn, params Func[] func); /// Add select columns. /// Columns to group by. /// Builder instance. IDynamicSelectQueryBuilder GroupByColumn(params DynamicColumn[] columns); /// Add select columns. /// Columns to group by. /// Column format consist of Column Name and /// Alias in this order separated by ':'. /// Builder instance. IDynamicSelectQueryBuilder GroupByColumn(params string[] columns); #endregion GroupBy #region Having /// /// Adds to the 'Having' clause the contents obtained from parsing the dynamic lambda expression given. The condition /// is parsed to the appropriate syntax, Having the specific customs virtual methods supported by the parser are used /// as needed. /// - If several Having() methods are chained their contents are, by default, concatenated with an 'AND' operator. /// - The 'And()' and 'Or()' virtual method can be used to concatenate with an 'OR' or an 'AND' operator, as in: /// 'Having( x => x.Or( condition ) )'. /// /// The specification. /// This instance to permit chaining. IDynamicSelectQueryBuilder Having(Func func); /// Add Having condition. /// Condition column with operator and value. /// Builder instance. IDynamicSelectQueryBuilder Having(DynamicColumn column); /// Add Having condition. /// Condition column. /// Condition operator. /// Condition value. /// Builder instance. IDynamicSelectQueryBuilder Having(string column, DynamicColumn.CompareOperator op, object value); /// Add Having condition. /// Condition column. /// Condition value. /// Builder instance. IDynamicSelectQueryBuilder Having(string column, object value); /// Add Having condition. /// Set conditions as properties and values of an object. /// If true use schema to determine key columns and ignore those which /// aren't keys. /// Builder instance. IDynamicSelectQueryBuilder Having(object conditions, bool schema = false); #endregion Having #region OrderBy /// /// Adds to the 'Order By' clause the contents obtained from from parsing the dynamic lambda expression given. It /// accepts a multipart column specification followed by an optional Ascending() or Descending() virtual methods /// to specify the direction. If no virtual method is used, the default is ascending order. You can also use the /// shorter versions Asc() and Desc(). /// /// The specification. /// The specification. /// This instance to permit chaining. IDynamicSelectQueryBuilder OrderBy(Func fn, params Func[] func); /// Add select columns. /// Columns to order by. /// Builder instance. IDynamicSelectQueryBuilder OrderByColumn(params DynamicColumn[] columns); /// Add select columns. /// Columns to order by. /// Column format consist of Column Name and /// Alias in this order separated by ':'. /// Builder instance. IDynamicSelectQueryBuilder OrderByColumn(params string[] columns); #endregion OrderBy #region Top/Limit/Offset/Distinct /// Set top if database support it. /// How many objects select. /// Builder instance. IDynamicSelectQueryBuilder Top(int? top); /// Set top if database support it. /// How many objects select. /// Builder instance. IDynamicSelectQueryBuilder Limit(int? limit); /// Set top if database support it. /// How many objects skip selecting. /// Builder instance. IDynamicSelectQueryBuilder Offset(int? offset); /// Set distinct mode. /// Distinct mode. /// Builder instance. IDynamicSelectQueryBuilder Distinct(bool distinct = true); #endregion Top/Limit/Offset/Distinct } /// Typed delete query builder for mapped entities. /// Mapped entity type. public interface IDynamicTypedDeleteQueryBuilder : IDynamicDeleteQueryBuilder { /// Add typed where predicate using mapped properties. /// Predicate to parse. /// Builder instance. IDynamicTypedDeleteQueryBuilder Where(Expression> predicate); /// Add typed SQL DSL where predicate. IDynamicTypedDeleteQueryBuilder WhereSql(Func, TypedSqlPredicate> predicate); } /// Typed insert query builder for mapped entities. /// Mapped entity type. public interface IDynamicTypedInsertQueryBuilder : IDynamicInsertQueryBuilder { /// Add typed insert assignment using mapped property selector. /// Property type. /// Property selector. /// Value to insert. /// Builder instance. IDynamicTypedInsertQueryBuilder Insert(Expression> selector, object value); /// Add values from mapped object. /// Mapped object value. /// Builder instance. IDynamicTypedInsertQueryBuilder Insert(T value); /// Add typed SQL DSL insert assignment. IDynamicTypedInsertQueryBuilder InsertSql(Expression> selector, Func, TypedSqlExpression> valueFactory); /// Add typed SQL DSL insert assignments from object projection. IDynamicTypedInsertQueryBuilder InsertSql(Func, object> values); } /// Typed select query builder for mapped entities. /// Mapped entity type. public interface IDynamicTypedSelectQueryBuilder : IDynamicSelectQueryBuilder { /// Add typed join to mapped table. /// Joined mapped entity type. /// Join ON predicate. /// Optional alias for joined table. /// Join type. /// Adds NOLOCK hint to joined source when supported by provider options. /// Builder instance. IDynamicTypedSelectQueryBuilder Join(Expression> on, string alias = null, DynamicJoinType joinType = DynamicJoinType.Inner, bool noLock = false); /// Add typed join with custom join type text (for example: CROSS APPLY). /// Joined mapped entity type. /// Optional join ON predicate. /// Optional alias for joined table. /// Join type text. /// Adds NOLOCK hint to joined source when supported by provider options. /// Builder instance. IDynamicTypedSelectQueryBuilder Join(Expression> on, string alias, string joinType, bool noLock = false); /// Add typed join using join-spec builder syntax (As(), join kind and On()). /// Joined mapped entity type. /// Join specification builder callback. /// Builder instance. IDynamicTypedSelectQueryBuilder Join(Func, TypedJoinBuilder> specification); /// Add typed where predicate using mapped properties. /// Predicate to parse. /// Builder instance. IDynamicTypedSelectQueryBuilder Where(Expression> predicate); /// Add typed having predicate using mapped properties. /// Predicate to parse. /// Builder instance. IDynamicTypedSelectQueryBuilder Having(Expression> predicate); /// Add typed selected columns using mapped properties. /// Projection type. /// Selector to parse. /// Additional selectors to parse. /// Builder instance. IDynamicTypedSelectQueryBuilder Select(Expression> selector, params Expression>[] selectors); /// Add typed group by columns using mapped properties. /// Projection type. /// Selector to parse. /// Additional selectors to parse. /// Builder instance. IDynamicTypedSelectQueryBuilder GroupBy(Expression> selector, params Expression>[] selectors); /// Add typed order by columns using mapped properties. Supports Asc()/Desc(). /// Projection type. /// Selector to parse. /// Additional selectors to parse. /// Builder instance. IDynamicTypedSelectQueryBuilder OrderBy(Expression> selector, params Expression>[] selectors); /// Add typed SQL DSL select items. IDynamicTypedSelectQueryBuilder SelectSql(Func, TypedSqlSelectable> selector, params Func, TypedSqlSelectable>[] selectors); /// Add typed SQL DSL where predicate. IDynamicTypedSelectQueryBuilder WhereSql(Func, TypedSqlPredicate> predicate); /// Add typed SQL DSL having predicate. IDynamicTypedSelectQueryBuilder HavingSql(Func, TypedSqlPredicate> predicate); /// Add typed SQL DSL group by expressions. IDynamicTypedSelectQueryBuilder GroupBySql(Func, TypedSqlExpression> selector, params Func, TypedSqlExpression>[] selectors); /// Add typed SQL DSL order by expressions. IDynamicTypedSelectQueryBuilder OrderBySql(Func, TypedSqlOrderExpression> selector, params Func, TypedSqlOrderExpression>[] selectors); /// Add typed SQL DSL select items using root and first joined table contexts. IDynamicTypedSelectQueryBuilder SelectSql(Func, TypedTableContext, TypedSqlSelectable> selector, params Func, TypedTableContext, TypedSqlSelectable>[] selectors); /// Add typed SQL DSL select items using root and first two joined table contexts. IDynamicTypedSelectQueryBuilder SelectSql(Func, TypedTableContext, TypedTableContext, TypedSqlSelectable> selector, params Func, TypedTableContext, TypedTableContext, TypedSqlSelectable>[] selectors); /// Add typed SQL DSL select items using root and first three joined table contexts. IDynamicTypedSelectQueryBuilder SelectSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable>[] selectors); /// Add typed SQL DSL select items using root and first four joined table contexts. IDynamicTypedSelectQueryBuilder SelectSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable>[] selectors); /// Add typed SQL DSL where predicate using root and first joined table contexts. IDynamicTypedSelectQueryBuilder WhereSql(Func, TypedTableContext, TypedSqlPredicate> predicate); /// Add typed SQL DSL where predicate using root and first two joined table contexts. IDynamicTypedSelectQueryBuilder WhereSql(Func, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate); /// Add typed SQL DSL where predicate using root and first three joined table contexts. IDynamicTypedSelectQueryBuilder WhereSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate); /// Add typed SQL DSL where predicate using root and first four joined table contexts. IDynamicTypedSelectQueryBuilder WhereSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate); /// Add typed SQL DSL having predicate using root and first joined table contexts. IDynamicTypedSelectQueryBuilder HavingSql(Func, TypedTableContext, TypedSqlPredicate> predicate); /// Add typed SQL DSL having predicate using root and first two joined table contexts. IDynamicTypedSelectQueryBuilder HavingSql(Func, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate); /// Add typed SQL DSL having predicate using root and first three joined table contexts. IDynamicTypedSelectQueryBuilder HavingSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate); /// Add typed SQL DSL having predicate using root and first four joined table contexts. IDynamicTypedSelectQueryBuilder HavingSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate); /// Add typed SQL DSL group-by expression using root and first joined table contexts. IDynamicTypedSelectQueryBuilder GroupBySql(Func, TypedTableContext, TypedSqlExpression> selector, params Func, TypedTableContext, TypedSqlExpression>[] selectors); /// Add typed SQL DSL group-by expression using root and first two joined table contexts. IDynamicTypedSelectQueryBuilder GroupBySql(Func, TypedTableContext, TypedTableContext, TypedSqlExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedSqlExpression>[] selectors); /// Add typed SQL DSL group-by expression using root and first three joined table contexts. IDynamicTypedSelectQueryBuilder GroupBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression>[] selectors); /// Add typed SQL DSL group-by expression using root and first four joined table contexts. IDynamicTypedSelectQueryBuilder GroupBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression>[] selectors); /// Add typed SQL DSL order-by expression using root and first joined table contexts. IDynamicTypedSelectQueryBuilder OrderBySql(Func, TypedTableContext, TypedSqlOrderExpression> selector, params Func, TypedTableContext, TypedSqlOrderExpression>[] selectors); /// Add typed SQL DSL order-by expression using root and first two joined table contexts. IDynamicTypedSelectQueryBuilder OrderBySql(Func, TypedTableContext, TypedTableContext, TypedSqlOrderExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedSqlOrderExpression>[] selectors); /// Add typed SQL DSL order-by expression using root and first three joined table contexts. IDynamicTypedSelectQueryBuilder OrderBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression>[] selectors); /// Add typed SQL DSL order-by expression using root and first four joined table contexts. IDynamicTypedSelectQueryBuilder OrderBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression>[] selectors); } public interface IDynamicTypedSelectScopeQueryBuilder : IDynamicSelectQueryBuilder { IDynamicTypedSelectScopeQueryBuilder Join(Func, TypedScopeJoinBuilder> specification); IDynamicTypedSelectScopeQueryBuilder SelectSql(Func, TypedSqlSelectable> selector, params Func, TypedSqlSelectable>[] selectors); IDynamicTypedSelectScopeQueryBuilder WhereSql(Func, TypedSqlPredicate> predicate); IDynamicTypedSelectScopeQueryBuilder HavingSql(Func, TypedSqlPredicate> predicate); IDynamicTypedSelectScopeQueryBuilder GroupBySql(Func, TypedSqlExpression> selector, params Func, TypedSqlExpression>[] selectors); IDynamicTypedSelectScopeQueryBuilder OrderBySql(Func, TypedSqlOrderExpression> selector, params Func, TypedSqlOrderExpression>[] selectors); } public interface IDynamicTypedSelectScopeQueryBuilder : IDynamicSelectQueryBuilder { IDynamicTypedSelectScopeQueryBuilder Join(Func, TypedScopeJoinBuilder> specification); IDynamicTypedSelectScopeQueryBuilder SelectSql(Func, TypedTableContext, TypedSqlSelectable> selector, params Func, TypedTableContext, TypedSqlSelectable>[] selectors); IDynamicTypedSelectScopeQueryBuilder WhereSql(Func, TypedTableContext, TypedSqlPredicate> predicate); IDynamicTypedSelectScopeQueryBuilder HavingSql(Func, TypedTableContext, TypedSqlPredicate> predicate); IDynamicTypedSelectScopeQueryBuilder GroupBySql(Func, TypedTableContext, TypedSqlExpression> selector, params Func, TypedTableContext, TypedSqlExpression>[] selectors); IDynamicTypedSelectScopeQueryBuilder OrderBySql(Func, TypedTableContext, TypedSqlOrderExpression> selector, params Func, TypedTableContext, TypedSqlOrderExpression>[] selectors); } public interface IDynamicTypedSelectScopeQueryBuilder : IDynamicSelectQueryBuilder { IDynamicTypedSelectScopeQueryBuilder Join(Func, TypedScopeJoinBuilder> specification); IDynamicTypedSelectScopeQueryBuilder SelectSql(Func, TypedTableContext, TypedTableContext, TypedSqlSelectable> selector, params Func, TypedTableContext, TypedTableContext, TypedSqlSelectable>[] selectors); IDynamicTypedSelectScopeQueryBuilder WhereSql(Func, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate); IDynamicTypedSelectScopeQueryBuilder HavingSql(Func, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate); IDynamicTypedSelectScopeQueryBuilder GroupBySql(Func, TypedTableContext, TypedTableContext, TypedSqlExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedSqlExpression>[] selectors); IDynamicTypedSelectScopeQueryBuilder OrderBySql(Func, TypedTableContext, TypedTableContext, TypedSqlOrderExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedSqlOrderExpression>[] selectors); } public interface IDynamicTypedSelectScopeQueryBuilder : IDynamicSelectQueryBuilder { IDynamicTypedSelectScopeQueryBuilder Join(Func, TypedScopeJoinBuilder> specification); IDynamicTypedSelectScopeQueryBuilder SelectSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable>[] selectors); IDynamicTypedSelectScopeQueryBuilder WhereSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate); IDynamicTypedSelectScopeQueryBuilder HavingSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate); IDynamicTypedSelectScopeQueryBuilder GroupBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression>[] selectors); IDynamicTypedSelectScopeQueryBuilder OrderBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression>[] selectors); } public interface IDynamicTypedSelectScopeQueryBuilder : IDynamicSelectQueryBuilder { IDynamicTypedSelectScopeQueryBuilder Join(Func, TypedScopeJoinBuilder> specification); IDynamicTypedSelectScopeQueryBuilder SelectSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable>[] selectors); IDynamicTypedSelectScopeQueryBuilder WhereSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate); IDynamicTypedSelectScopeQueryBuilder HavingSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate); IDynamicTypedSelectScopeQueryBuilder GroupBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression>[] selectors); IDynamicTypedSelectScopeQueryBuilder OrderBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression>[] selectors); } public interface IDynamicTypedSelectScopeQueryBuilder : IDynamicSelectQueryBuilder { IDynamicTypedSelectScopeQueryBuilder Join(Func, TypedScopeJoinBuilder> specification); IDynamicTypedSelectScopeQueryBuilder SelectSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable>[] selectors); IDynamicTypedSelectScopeQueryBuilder WhereSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate); IDynamicTypedSelectScopeQueryBuilder HavingSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate); IDynamicTypedSelectScopeQueryBuilder GroupBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression>[] selectors); IDynamicTypedSelectScopeQueryBuilder OrderBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression>[] selectors); } public interface IDynamicTypedSelectScopeQueryBuilder : IDynamicSelectQueryBuilder { IDynamicTypedSelectScopeQueryBuilder Join(Func, TypedScopeJoinBuilder> specification); IDynamicTypedSelectScopeQueryBuilder SelectSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable>[] selectors); IDynamicTypedSelectScopeQueryBuilder WhereSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate); IDynamicTypedSelectScopeQueryBuilder HavingSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate); IDynamicTypedSelectScopeQueryBuilder GroupBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression>[] selectors); IDynamicTypedSelectScopeQueryBuilder OrderBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression>[] selectors); } public interface IDynamicTypedSelectScopeQueryBuilder : IDynamicSelectQueryBuilder { IDynamicTypedSelectScopeQueryBuilder Join(Func, TypedScopeJoinBuilder> specification); IDynamicTypedSelectScopeQueryBuilder SelectSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable>[] selectors); IDynamicTypedSelectScopeQueryBuilder WhereSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate); IDynamicTypedSelectScopeQueryBuilder HavingSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate); IDynamicTypedSelectScopeQueryBuilder GroupBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression>[] selectors); IDynamicTypedSelectScopeQueryBuilder OrderBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression>[] selectors); } public interface IDynamicTypedSelectScopeQueryBuilder : IDynamicSelectQueryBuilder { IDynamicTypedSelectScopeQueryBuilder Join(Func, TypedScopeJoinBuilder> specification); IDynamicTypedSelectScopeQueryBuilder SelectSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable>[] selectors); IDynamicTypedSelectScopeQueryBuilder WhereSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate); IDynamicTypedSelectScopeQueryBuilder HavingSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate); IDynamicTypedSelectScopeQueryBuilder GroupBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression>[] selectors); IDynamicTypedSelectScopeQueryBuilder OrderBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression>[] selectors); } public interface IDynamicTypedSelectScopeQueryBuilder : IDynamicSelectQueryBuilder { IDynamicTypedSelectScopeQueryBuilder Join(Func, TypedScopeJoinBuilder> specification); IDynamicTypedSelectScopeQueryBuilder SelectSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable>[] selectors); IDynamicTypedSelectScopeQueryBuilder WhereSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate); IDynamicTypedSelectScopeQueryBuilder HavingSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate); IDynamicTypedSelectScopeQueryBuilder GroupBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression>[] selectors); IDynamicTypedSelectScopeQueryBuilder OrderBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression>[] selectors); } public interface IDynamicTypedSelectScopeQueryBuilder : IDynamicSelectQueryBuilder { IDynamicTypedSelectScopeQueryBuilder Join(Func, TypedScopeJoinBuilder> specification); IDynamicTypedSelectScopeQueryBuilder SelectSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable>[] selectors); IDynamicTypedSelectScopeQueryBuilder WhereSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate); IDynamicTypedSelectScopeQueryBuilder HavingSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate); IDynamicTypedSelectScopeQueryBuilder GroupBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression>[] selectors); IDynamicTypedSelectScopeQueryBuilder OrderBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression>[] selectors); } public interface IDynamicTypedSelectScopeQueryBuilder : IDynamicSelectQueryBuilder { IDynamicTypedSelectScopeQueryBuilder Join(Func, TypedScopeJoinBuilder> specification); IDynamicTypedSelectScopeQueryBuilder SelectSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable>[] selectors); IDynamicTypedSelectScopeQueryBuilder WhereSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate); IDynamicTypedSelectScopeQueryBuilder HavingSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate); IDynamicTypedSelectScopeQueryBuilder GroupBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression>[] selectors); IDynamicTypedSelectScopeQueryBuilder OrderBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression>[] selectors); } public interface IDynamicTypedSelectScopeQueryBuilder : IDynamicSelectQueryBuilder { IDynamicTypedSelectScopeQueryBuilder Join(Func, TypedScopeJoinBuilder> specification); IDynamicTypedSelectScopeQueryBuilder SelectSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable>[] selectors); IDynamicTypedSelectScopeQueryBuilder WhereSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate); IDynamicTypedSelectScopeQueryBuilder HavingSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate); IDynamicTypedSelectScopeQueryBuilder GroupBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression>[] selectors); IDynamicTypedSelectScopeQueryBuilder OrderBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression>[] selectors); } public interface IDynamicTypedSelectScopeQueryBuilder : IDynamicSelectQueryBuilder { IDynamicTypedSelectScopeQueryBuilder Join(Func, TypedScopeJoinBuilder> specification); IDynamicTypedSelectScopeQueryBuilder SelectSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable>[] selectors); IDynamicTypedSelectScopeQueryBuilder WhereSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate); IDynamicTypedSelectScopeQueryBuilder HavingSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate); IDynamicTypedSelectScopeQueryBuilder GroupBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression>[] selectors); IDynamicTypedSelectScopeQueryBuilder OrderBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression>[] selectors); } public interface IDynamicTypedSelectScopeQueryBuilder : IDynamicSelectQueryBuilder { IDynamicTypedSelectScopeQueryBuilder Join(Func, TypedScopeJoinBuilder> specification); IDynamicTypedSelectScopeQueryBuilder SelectSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable>[] selectors); IDynamicTypedSelectScopeQueryBuilder WhereSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate); IDynamicTypedSelectScopeQueryBuilder HavingSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate); IDynamicTypedSelectScopeQueryBuilder GroupBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression>[] selectors); IDynamicTypedSelectScopeQueryBuilder OrderBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression>[] selectors); } public interface IDynamicTypedSelectScopeQueryBuilder : IDynamicSelectQueryBuilder { IDynamicTypedSelectScopeQueryBuilder SelectSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable>[] selectors); IDynamicTypedSelectScopeQueryBuilder WhereSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate); IDynamicTypedSelectScopeQueryBuilder HavingSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate); IDynamicTypedSelectScopeQueryBuilder GroupBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression>[] selectors); IDynamicTypedSelectScopeQueryBuilder OrderBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression>[] selectors); } /// Typed update query builder for mapped entities. /// Mapped entity type. public interface IDynamicTypedUpdateQueryBuilder : IDynamicUpdateQueryBuilder { /// Add typed where predicate using mapped properties. /// Predicate to parse. /// Builder instance. IDynamicTypedUpdateQueryBuilder Where(Expression> predicate); /// Add typed assignment using mapped property selector. /// Property type. /// Property selector. /// Assigned value. /// Builder instance. IDynamicTypedUpdateQueryBuilder Set(Expression> selector, object value); /// Add update values from mapped object. /// Mapped object value. /// Builder instance. IDynamicTypedUpdateQueryBuilder Values(T value); /// Add typed SQL DSL where predicate. IDynamicTypedUpdateQueryBuilder WhereSql(Func, TypedSqlPredicate> predicate); /// Add typed SQL DSL assignment. IDynamicTypedUpdateQueryBuilder SetSql(Expression> selector, Func, TypedSqlExpression> valueFactory); /// Add typed SQL DSL assignments from object projection. IDynamicTypedUpdateQueryBuilder SetSql(Func, object> values); } /// Dynamic update query builder interface. /// This interface it publicly available. Implementation should be hidden. public interface IDynamicUpdateQueryBuilder : IDynamicQueryBuilder { /// Execute this builder. /// Result of an execution.. int Execute(); #region Update /// Add update value or where condition using schema. /// Update or where column name. /// Column value. /// Builder instance. IDynamicUpdateQueryBuilder Update(string column, object value); /// Add update values and where condition columns using schema. /// Set values or conditions as properties and values of an object. /// Builder instance. IDynamicUpdateQueryBuilder Update(object conditions); #endregion Update #region Values /// /// Specifies the columns to update using the dynamic lambda expressions given. Each expression correspond to one /// column, and can: /// - Resolve to a string, in this case a '=' must appear in the string. /// - Resolve to a expression with the form: 'x => x.Column = Value'. /// /// The specifications. /// This instance to permit chaining. IDynamicUpdateQueryBuilder Set(params Func[] func); /// Add insert fields. /// Insert column. /// Insert value. /// Builder instance. IDynamicUpdateQueryBuilder Values(string column, object value); /// Add insert fields. /// Set insert value as properties and values of an object. /// Builder instance. IDynamicUpdateQueryBuilder Values(object o); #endregion Values #region Where /// /// Adds to the 'Where' clause the contents obtained from parsing the dynamic lambda expression given. The condition /// is parsed to the appropriate syntax, where the specific customs virtual methods supported by the parser are used /// as needed. /// - If several Where() methods are chained their contents are, by default, concatenated with an 'AND' operator. /// - The 'And()' and 'Or()' virtual method can be used to concatenate with an 'OR' or an 'AND' operator, as in: /// 'Where( x => x.Or( condition ) )'. /// /// The specification. /// This instance to permit chaining. IDynamicUpdateQueryBuilder Where(Func func); /// Add where condition. /// Condition column with operator and value. /// Builder instance. IDynamicUpdateQueryBuilder Where(DynamicColumn column); /// Add where condition. /// Condition column. /// Condition operator. /// Condition value. /// Builder instance. IDynamicUpdateQueryBuilder Where(string column, DynamicColumn.CompareOperator op, object value); /// Add where condition. /// Condition column. /// Condition value. /// Builder instance. IDynamicUpdateQueryBuilder Where(string column, object value); /// Add where condition. /// Set conditions as properties and values of an object. /// If true use schema to determine key columns and ignore those which /// aren't keys. /// Builder instance. IDynamicUpdateQueryBuilder Where(object conditions, bool schema = false); #endregion Where } /// Interface describing parameter info. public interface IParameter : IExtendedDisposable { /// Gets the parameter position in command. /// Available after filling the command. int Ordinal { get; } /// Gets the parameter temporary name. string Name { get; } /// Gets or sets the parameter value. object Value { get; set; } /// Gets or sets a value indicating whether name of temporary parameter is well known. bool WellKnown { get; set; } /// Gets or sets a value indicating whether this is virtual. bool Virtual { get; set; } /// Gets or sets the parameter schema information. DynamicSchemaColumn? Schema { get; set; } } /// Interface describing table information. public interface ITableInfo : IExtendedDisposable { /// Gets table owner name. string Owner { get; } /// Gets table name. string Name { get; } /// Gets table alias. string Alias { get; } /// Gets table no lock status. bool NoLock { get; } /// Gets table schema. Dictionary Schema { get; } } /// Marker extensions for typed fluent builder expressions. public static class TypedFluentExtensions { /// Marks select projection alias in typed expressions. public static T As(this T source, string alias) { return source; } /// Marks ascending order in typed order expressions. public static T Asc(this T source) { return source; } /// Marks descending order in typed order expressions. public static T Desc(this T source) { return source; } } /// Typed join specification builder used by typed fluent select queries. /// Left side mapped type. /// Right side mapped type. public class TypedJoinBuilder { internal TypedJoinBuilder() { JoinType = DynamicJoinType.Join; } /// Gets join alias. public string Alias { get; private set; } /// Gets join type. public DynamicJoinType JoinType { get; private set; } /// Gets custom join type text. public string CustomJoinType { get; private set; } /// Gets a value indicating whether joined source should use NOLOCK. public bool UseNoLock { get; private set; } /// Gets ON predicate. public Expression> OnPredicate { get; private set; } /// Gets raw ON condition. public string OnRawCondition { get; private set; } /// Gets typed SQL DSL ON specification. public Func, TypedTableContext, TypedSqlPredicate> OnSqlPredicate { get; private set; } /// Sets join alias. public TypedJoinBuilder As(string alias) { Alias = alias; return this; } /// Sets INNER JOIN. public TypedJoinBuilder Inner() { JoinType = DynamicJoinType.Inner; CustomJoinType = null; return this; } /// Sets plain JOIN. public TypedJoinBuilder Join() { JoinType = DynamicJoinType.Join; CustomJoinType = null; return this; } /// Sets LEFT JOIN. public TypedJoinBuilder Left() { JoinType = DynamicJoinType.Left; CustomJoinType = null; return this; } /// Sets RIGHT JOIN. public TypedJoinBuilder Right() { JoinType = DynamicJoinType.Right; CustomJoinType = null; return this; } /// Sets FULL JOIN. public TypedJoinBuilder Full() { JoinType = DynamicJoinType.Full; CustomJoinType = null; return this; } /// Sets LEFT OUTER JOIN. public TypedJoinBuilder LeftOuter() { JoinType = DynamicJoinType.LeftOuter; CustomJoinType = null; return this; } /// Sets RIGHT OUTER JOIN. public TypedJoinBuilder RightOuter() { JoinType = DynamicJoinType.RightOuter; CustomJoinType = null; return this; } /// Sets FULL OUTER JOIN. public TypedJoinBuilder FullOuter() { JoinType = DynamicJoinType.FullOuter; CustomJoinType = null; return this; } /// Sets custom join type text (for example: CROSS APPLY). public TypedJoinBuilder Type(string joinType) { if (string.IsNullOrEmpty(joinType)) throw new ArgumentNullException("joinType"); CustomJoinType = joinType.Trim(); return this; } /// Marks joined source with NOLOCK hint. public TypedJoinBuilder NoLock(bool use = true) { UseNoLock = use; return this; } /// Sets ON predicate. public TypedJoinBuilder On(Expression> predicate) { if (predicate == null) throw new ArgumentNullException("predicate"); OnPredicate = predicate; OnRawCondition = null; OnSqlPredicate = null; return this; } /// Sets raw ON clause (without the ON keyword). public TypedJoinBuilder OnRaw(string condition) { if (string.IsNullOrEmpty(condition)) throw new ArgumentNullException("condition"); OnRawCondition = condition.Trim(); OnPredicate = null; OnSqlPredicate = null; return this; } /// Sets typed SQL DSL ON predicate. public TypedJoinBuilder OnSql(Func, TypedTableContext, TypedSqlPredicate> predicate) { if (predicate == null) throw new ArgumentNullException("predicate"); OnSqlPredicate = predicate; OnPredicate = null; OnRawCondition = null; return this; } } /// Compatibility and convenience extensions for typed joins. public static class TypedJoinExtensions { /// /// Legacy compatibility helper. Prefer . /// public static IDynamicTypedSelectQueryBuilder JoinTyped( this IDynamicTypedSelectQueryBuilder builder, string alias, Expression> on, string joinType = "INNER JOIN") { if (builder == null) throw new ArgumentNullException("builder"); if (on == null) throw new ArgumentNullException("on"); DynamicJoinType jt = DynamicJoinType.Inner; string normalized = (joinType ?? string.Empty).Trim().ToUpperInvariant(); if (normalized == "JOIN") jt = DynamicJoinType.Join; else if (normalized == "LEFT OUTER JOIN" || normalized == "LEFT OUTER") jt = DynamicJoinType.LeftOuter; else if (normalized == "RIGHT OUTER JOIN" || normalized == "RIGHT OUTER") jt = DynamicJoinType.RightOuter; else if (normalized == "FULL OUTER JOIN" || normalized == "FULL OUTER") jt = DynamicJoinType.FullOuter; else if (normalized == "LEFT JOIN" || normalized == "LEFT") jt = DynamicJoinType.Left; else if (normalized == "RIGHT JOIN" || normalized == "RIGHT") jt = DynamicJoinType.Right; else if (normalized == "FULL JOIN" || normalized == "FULL") jt = DynamicJoinType.Full; else if (normalized != "INNER JOIN" && normalized != "INNER" && normalized != "JOIN") return builder.Join(on, alias, joinType); return builder.Join(on, alias, jt); } /// Convenience typed INNER JOIN extension. public static IDynamicTypedSelectQueryBuilder InnerJoinTyped( this IDynamicTypedSelectQueryBuilder builder, Expression> on, string alias = null) { return builder.Join(on, alias, DynamicJoinType.Inner); } /// Convenience typed LEFT JOIN extension. public static IDynamicTypedSelectQueryBuilder LeftJoinTyped( this IDynamicTypedSelectQueryBuilder builder, Expression> on, string alias = null) { return builder.Join(on, alias, DynamicJoinType.Left); } /// Convenience typed RIGHT JOIN extension. public static IDynamicTypedSelectQueryBuilder RightJoinTyped( this IDynamicTypedSelectQueryBuilder builder, Expression> on, string alias = null) { return builder.Join(on, alias, DynamicJoinType.Right); } /// Convenience typed FULL JOIN extension. public static IDynamicTypedSelectQueryBuilder FullJoinTyped( this IDynamicTypedSelectQueryBuilder builder, Expression> on, string alias = null) { return builder.Join(on, alias, DynamicJoinType.Full); } } public abstract class TypedScopeJoinBuilderBase where TSelf : TypedScopeJoinBuilderBase { public string Alias { get; private set; } public DynamicJoinType JoinType { get; private set; } public string CustomJoinType { get; private set; } public bool UseNoLock { get; private set; } protected TypedScopeJoinBuilderBase() { JoinType = DynamicJoinType.Join; } public TSelf As(string alias) { Alias = alias; return (TSelf)this; } public TSelf Inner() { JoinType = DynamicJoinType.Inner; CustomJoinType = null; return (TSelf)this; } public TSelf Join() { JoinType = DynamicJoinType.Join; CustomJoinType = null; return (TSelf)this; } public TSelf Left() { JoinType = DynamicJoinType.Left; CustomJoinType = null; return (TSelf)this; } public TSelf Right() { JoinType = DynamicJoinType.Right; CustomJoinType = null; return (TSelf)this; } public TSelf Full() { JoinType = DynamicJoinType.Full; CustomJoinType = null; return (TSelf)this; } public TSelf LeftOuter() { JoinType = DynamicJoinType.LeftOuter; CustomJoinType = null; return (TSelf)this; } public TSelf RightOuter() { JoinType = DynamicJoinType.RightOuter; CustomJoinType = null; return (TSelf)this; } public TSelf FullOuter() { JoinType = DynamicJoinType.FullOuter; CustomJoinType = null; return (TSelf)this; } public TSelf Type(string joinType) { if (string.IsNullOrEmpty(joinType)) throw new ArgumentNullException("joinType"); CustomJoinType = joinType.Trim(); return (TSelf)this; } public TSelf NoLock(bool use = true) { UseNoLock = use; return (TSelf)this; } } public sealed class TypedScopeJoinBuilder : TypedScopeJoinBuilderBase> { internal Func, TypedTableContext, TypedSqlPredicate> OnSqlPredicate { get; private set; } internal string OnRawCondition { get; private set; } public TypedScopeJoinBuilder OnSql(Func, TypedTableContext, TypedSqlPredicate> predicate) { if (predicate == null) throw new ArgumentNullException("predicate"); OnSqlPredicate = predicate; OnRawCondition = null; return this; } public TypedScopeJoinBuilder OnRaw(string condition) { if (string.IsNullOrEmpty(condition)) throw new ArgumentNullException("condition"); OnRawCondition = condition.Trim(); OnSqlPredicate = null; return this; } } public sealed class TypedScopeJoinBuilder : TypedScopeJoinBuilderBase> { internal Func, TypedTableContext, TypedTableContext, TypedSqlPredicate> OnSqlPredicate { get; private set; } internal string OnRawCondition { get; private set; } public TypedScopeJoinBuilder OnSql(Func, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate) { if (predicate == null) throw new ArgumentNullException("predicate"); OnSqlPredicate = predicate; OnRawCondition = null; return this; } public TypedScopeJoinBuilder OnRaw(string condition) { if (string.IsNullOrEmpty(condition)) throw new ArgumentNullException("condition"); OnRawCondition = condition.Trim(); OnSqlPredicate = null; return this; } } public sealed class TypedScopeJoinBuilder : TypedScopeJoinBuilderBase> { internal Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> OnSqlPredicate { get; private set; } internal string OnRawCondition { get; private set; } public TypedScopeJoinBuilder OnSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate) { if (predicate == null) throw new ArgumentNullException("predicate"); OnSqlPredicate = predicate; OnRawCondition = null; return this; } public TypedScopeJoinBuilder OnRaw(string condition) { if (string.IsNullOrEmpty(condition)) throw new ArgumentNullException("condition"); OnRawCondition = condition.Trim(); OnSqlPredicate = null; return this; } } public sealed class TypedScopeJoinBuilder : TypedScopeJoinBuilderBase> { internal Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> OnSqlPredicate { get; private set; } internal string OnRawCondition { get; private set; } public TypedScopeJoinBuilder OnSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate) { if (predicate == null) throw new ArgumentNullException("predicate"); OnSqlPredicate = predicate; OnRawCondition = null; return this; } public TypedScopeJoinBuilder OnRaw(string condition) { if (string.IsNullOrEmpty(condition)) throw new ArgumentNullException("condition"); OnRawCondition = condition.Trim(); OnSqlPredicate = null; return this; } } public sealed class TypedScopeJoinBuilder : TypedScopeJoinBuilderBase> { internal Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> OnSqlPredicate { get; private set; } internal string OnRawCondition { get; private set; } public TypedScopeJoinBuilder OnSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate) { if (predicate == null) throw new ArgumentNullException("predicate"); OnSqlPredicate = predicate; OnRawCondition = null; return this; } public TypedScopeJoinBuilder OnRaw(string condition) { if (string.IsNullOrEmpty(condition)) throw new ArgumentNullException("condition"); OnRawCondition = condition.Trim(); OnSqlPredicate = null; return this; } } public sealed class TypedScopeJoinBuilder : TypedScopeJoinBuilderBase> { internal Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> OnSqlPredicate { get; private set; } internal string OnRawCondition { get; private set; } public TypedScopeJoinBuilder OnSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate) { if (predicate == null) throw new ArgumentNullException("predicate"); OnSqlPredicate = predicate; OnRawCondition = null; return this; } public TypedScopeJoinBuilder OnRaw(string condition) { if (string.IsNullOrEmpty(condition)) throw new ArgumentNullException("condition"); OnRawCondition = condition.Trim(); OnSqlPredicate = null; return this; } } public sealed class TypedScopeJoinBuilder : TypedScopeJoinBuilderBase> { internal Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> OnSqlPredicate { get; private set; } internal string OnRawCondition { get; private set; } public TypedScopeJoinBuilder OnSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate) { if (predicate == null) throw new ArgumentNullException("predicate"); OnSqlPredicate = predicate; OnRawCondition = null; return this; } public TypedScopeJoinBuilder OnRaw(string condition) { if (string.IsNullOrEmpty(condition)) throw new ArgumentNullException("condition"); OnRawCondition = condition.Trim(); OnSqlPredicate = null; return this; } } public sealed class TypedScopeJoinBuilder : TypedScopeJoinBuilderBase> { internal Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> OnSqlPredicate { get; private set; } internal string OnRawCondition { get; private set; } public TypedScopeJoinBuilder OnSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate) { if (predicate == null) throw new ArgumentNullException("predicate"); OnSqlPredicate = predicate; OnRawCondition = null; return this; } public TypedScopeJoinBuilder OnRaw(string condition) { if (string.IsNullOrEmpty(condition)) throw new ArgumentNullException("condition"); OnRawCondition = condition.Trim(); OnSqlPredicate = null; return this; } } public sealed class TypedScopeJoinBuilder : TypedScopeJoinBuilderBase> { internal Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> OnSqlPredicate { get; private set; } internal string OnRawCondition { get; private set; } public TypedScopeJoinBuilder OnSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate) { if (predicate == null) throw new ArgumentNullException("predicate"); OnSqlPredicate = predicate; OnRawCondition = null; return this; } public TypedScopeJoinBuilder OnRaw(string condition) { if (string.IsNullOrEmpty(condition)) throw new ArgumentNullException("condition"); OnRawCondition = condition.Trim(); OnSqlPredicate = null; return this; } } public sealed class TypedScopeJoinBuilder : TypedScopeJoinBuilderBase> { internal Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> OnSqlPredicate { get; private set; } internal string OnRawCondition { get; private set; } public TypedScopeJoinBuilder OnSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate) { if (predicate == null) throw new ArgumentNullException("predicate"); OnSqlPredicate = predicate; OnRawCondition = null; return this; } public TypedScopeJoinBuilder OnRaw(string condition) { if (string.IsNullOrEmpty(condition)) throw new ArgumentNullException("condition"); OnRawCondition = condition.Trim(); OnSqlPredicate = null; return this; } } public sealed class TypedScopeJoinBuilder : TypedScopeJoinBuilderBase> { internal Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> OnSqlPredicate { get; private set; } internal string OnRawCondition { get; private set; } public TypedScopeJoinBuilder OnSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate) { if (predicate == null) throw new ArgumentNullException("predicate"); OnSqlPredicate = predicate; OnRawCondition = null; return this; } public TypedScopeJoinBuilder OnRaw(string condition) { if (string.IsNullOrEmpty(condition)) throw new ArgumentNullException("condition"); OnRawCondition = condition.Trim(); OnSqlPredicate = null; return this; } } public sealed class TypedScopeJoinBuilder : TypedScopeJoinBuilderBase> { internal Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> OnSqlPredicate { get; private set; } internal string OnRawCondition { get; private set; } public TypedScopeJoinBuilder OnSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate) { if (predicate == null) throw new ArgumentNullException("predicate"); OnSqlPredicate = predicate; OnRawCondition = null; return this; } public TypedScopeJoinBuilder OnRaw(string condition) { if (string.IsNullOrEmpty(condition)) throw new ArgumentNullException("condition"); OnRawCondition = condition.Trim(); OnSqlPredicate = null; return this; } } public sealed class TypedScopeJoinBuilder : TypedScopeJoinBuilderBase> { internal Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> OnSqlPredicate { get; private set; } internal string OnRawCondition { get; private set; } public TypedScopeJoinBuilder OnSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate) { if (predicate == null) throw new ArgumentNullException("predicate"); OnSqlPredicate = predicate; OnRawCondition = null; return this; } public TypedScopeJoinBuilder OnRaw(string condition) { if (string.IsNullOrEmpty(condition)) throw new ArgumentNullException("condition"); OnRawCondition = condition.Trim(); OnSqlPredicate = null; return this; } } public sealed class TypedScopeJoinBuilder : TypedScopeJoinBuilderBase> { internal Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> OnSqlPredicate { get; private set; } internal string OnRawCondition { get; private set; } public TypedScopeJoinBuilder OnSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate) { if (predicate == null) throw new ArgumentNullException("predicate"); OnSqlPredicate = predicate; OnRawCondition = null; return this; } public TypedScopeJoinBuilder OnRaw(string condition) { if (string.IsNullOrEmpty(condition)) throw new ArgumentNullException("condition"); OnRawCondition = condition.Trim(); OnSqlPredicate = null; return this; } } public sealed class TypedScopeJoinBuilder : TypedScopeJoinBuilderBase> { internal Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> OnSqlPredicate { get; private set; } internal string OnRawCondition { get; private set; } public TypedScopeJoinBuilder OnSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate) { if (predicate == null) throw new ArgumentNullException("predicate"); OnSqlPredicate = predicate; OnRawCondition = null; return this; } public TypedScopeJoinBuilder OnRaw(string condition) { if (string.IsNullOrEmpty(condition)) throw new ArgumentNullException("condition"); OnRawCondition = condition.Trim(); OnSqlPredicate = null; return this; } } namespace Extensions { internal static class DynamicHavingQueryExtensions { #region Where internal static T InternalHaving(this T builder, Func func) where T : DynamicQueryBuilder, DynamicQueryBuilder.IQueryWithHaving { return builder.InternalHaving(false, false, func); } internal static T InternalHaving(this T builder, bool addBeginBrace, bool addEndBrace, Func func) where T : DynamicQueryBuilder, DynamicQueryBuilder.IQueryWithHaving { if (func == null) throw new ArgumentNullException("Array of functions cannot be null."); using (DynamicParser parser = DynamicParser.Parse(func)) { string condition = null; bool and = true; object result = parser.Result; if (result is string) { condition = (string)result; if (condition.ToUpper().IndexOf("OR") == 0) { and = false; condition = condition.Substring(3); } else if (condition.ToUpper().IndexOf("AND") == 0) condition = condition.Substring(4); } else if (!(result is DynamicParser.Node) && !result.GetType().IsValueType) return builder.InternalHaving(result); else { // Intercepting the 'x => x.And()' and 'x => x.Or()' virtual methods... if (result is DynamicParser.Node.Method && ((DynamicParser.Node.Method)result).Host is DynamicParser.Node.Argument) { DynamicParser.Node.Method node = (DynamicParser.Node.Method)result; string name = node.Name.ToUpper(); if (name == "AND" || name == "OR") { object[] args = ((DynamicParser.Node.Method)node).Arguments; if (args == null) throw new ArgumentNullException("arg", string.Format("{0} is not a parameterless method.", name)); if (args.Length != 1) throw new ArgumentException(string.Format("{0} requires one and only one parameter: {1}.", name, args.Sketch())); and = name == "AND" ? true : false; result = args[0]; } } // Just parsing the contents now... condition = builder.Parse(result, pars: builder.Parameters).Validated("Where condition"); } if (addBeginBrace) builder.HavingOpenBracketsCount++; if (addEndBrace) builder.HavingOpenBracketsCount--; if (builder.HavingCondition == null) builder.HavingCondition = string.Format("{0}{1}{2}", addBeginBrace ? "(" : string.Empty, condition, addEndBrace ? ")" : string.Empty); else builder.HavingCondition = string.Format("{0} {1} {2}{3}{4}", builder.HavingCondition, and ? "AND" : "OR", addBeginBrace ? "(" : string.Empty, condition, addEndBrace ? ")" : string.Empty); } return builder; } internal static T InternalHaving(this T builder, DynamicColumn column) where T : DynamicQueryBuilder, DynamicQueryBuilder.IQueryWithHaving { bool virt = builder.VirtualMode; if (column.VirtualColumn.HasValue) builder.VirtualMode = column.VirtualColumn.Value; Action modParam = (p) => { if (column.Schema.HasValue) p.Schema = column.Schema; if (!p.Schema.HasValue) p.Schema = column.Schema ?? builder.GetColumnFromSchema(column.ColumnName); }; builder.CreateTemporaryParameterAction(modParam); // It's kind of uglu, but... well it works. if (column.Or) switch (column.Operator) { default: case DynamicColumn.CompareOperator.Eq: builder.InternalHaving(column.BeginBlock, column.EndBlock, x => x.Or(x(builder.FixObjectName(column.ColumnName)) == column.Value)); break; case DynamicColumn.CompareOperator.Not: builder.InternalHaving(column.BeginBlock, column.EndBlock, x => x.Or(x(builder.FixObjectName(column.ColumnName)) != column.Value)); break; case DynamicColumn.CompareOperator.Like: builder.InternalHaving(column.BeginBlock, column.EndBlock, x => x.Or(x(builder.FixObjectName(column.ColumnName)).Like(column.Value))); break; case DynamicColumn.CompareOperator.NotLike: builder.InternalHaving(column.BeginBlock, column.EndBlock, x => x.Or(x(builder.FixObjectName(column.ColumnName)).NotLike(column.Value))); break; case DynamicColumn.CompareOperator.In: builder.InternalHaving(column.BeginBlock, column.EndBlock, x => x.Or(x(builder.FixObjectName(column.ColumnName)).In(column.Value))); break; case DynamicColumn.CompareOperator.Lt: builder.InternalHaving(column.BeginBlock, column.EndBlock, x => x.Or(x(builder.FixObjectName(column.ColumnName)) < column.Value)); break; case DynamicColumn.CompareOperator.Lte: builder.InternalHaving(column.BeginBlock, column.EndBlock, x => x.Or(x(builder.FixObjectName(column.ColumnName)) <= column.Value)); break; case DynamicColumn.CompareOperator.Gt: builder.InternalHaving(column.BeginBlock, column.EndBlock, x => x.Or(x(builder.FixObjectName(column.ColumnName)) > column.Value)); break; case DynamicColumn.CompareOperator.Gte: builder.InternalHaving(column.BeginBlock, column.EndBlock, x => x.Or(x(builder.FixObjectName(column.ColumnName)) >= column.Value)); break; case DynamicColumn.CompareOperator.Between: builder.InternalHaving(column.BeginBlock, column.EndBlock, x => x.Or(x(builder.FixObjectName(column.ColumnName)).Between(column.Value))); break; } else switch (column.Operator) { default: case DynamicColumn.CompareOperator.Eq: builder.InternalHaving(column.BeginBlock, column.EndBlock, x => x(builder.FixObjectName(column.ColumnName)) == column.Value); break; case DynamicColumn.CompareOperator.Not: builder.InternalHaving(column.BeginBlock, column.EndBlock, x => x(builder.FixObjectName(column.ColumnName)) != column.Value); break; case DynamicColumn.CompareOperator.Like: builder.InternalHaving(column.BeginBlock, column.EndBlock, x => x(builder.FixObjectName(column.ColumnName)).Like(column.Value)); break; case DynamicColumn.CompareOperator.NotLike: builder.InternalHaving(column.BeginBlock, column.EndBlock, x => x(builder.FixObjectName(column.ColumnName)).NotLike(column.Value)); break; case DynamicColumn.CompareOperator.In: builder.InternalHaving(column.BeginBlock, column.EndBlock, x => x(builder.FixObjectName(column.ColumnName)).In(column.Value)); break; case DynamicColumn.CompareOperator.Lt: builder.InternalHaving(column.BeginBlock, column.EndBlock, x => x(builder.FixObjectName(column.ColumnName)) < column.Value); break; case DynamicColumn.CompareOperator.Lte: builder.InternalHaving(column.BeginBlock, column.EndBlock, x => x(builder.FixObjectName(column.ColumnName)) <= column.Value); break; case DynamicColumn.CompareOperator.Gt: builder.InternalHaving(column.BeginBlock, column.EndBlock, x => x(builder.FixObjectName(column.ColumnName)) > column.Value); break; case DynamicColumn.CompareOperator.Gte: builder.InternalHaving(column.BeginBlock, column.EndBlock, x => x(builder.FixObjectName(column.ColumnName)) >= column.Value); break; case DynamicColumn.CompareOperator.Between: builder.InternalHaving(column.BeginBlock, column.EndBlock, x => x(builder.FixObjectName(column.ColumnName)).Between(column.Value)); break; } builder.OnCreateTemporaryParameter.Remove(modParam); builder.VirtualMode = virt; return builder; } internal static T InternalHaving(this T builder, string column, DynamicColumn.CompareOperator op, object value) where T : DynamicQueryBuilder, DynamicQueryBuilder.IQueryWithHaving { if (value is DynamicColumn) { DynamicColumn v = (DynamicColumn)value; if (string.IsNullOrEmpty(v.ColumnName)) v.ColumnName = column; return builder.InternalHaving(v); } else if (value is IEnumerable) { foreach (DynamicColumn v in (IEnumerable)value) builder.InternalHaving(v); return builder; } return builder.InternalHaving(new DynamicColumn { ColumnName = column, Operator = op, Value = value }); } internal static T InternalHaving(this T builder, string column, object value) where T : DynamicQueryBuilder, DynamicQueryBuilder.IQueryWithHaving { return builder.InternalHaving(column, DynamicColumn.CompareOperator.Eq, value); } internal static T InternalHaving(this T builder, object conditions, bool schema = false) where T : DynamicQueryBuilder, DynamicQueryBuilder.IQueryWithHaving { if (conditions is DynamicColumn) return builder.InternalHaving((DynamicColumn)conditions); else if (conditions is IEnumerable) { foreach (DynamicColumn v in (IEnumerable)conditions) builder.InternalHaving(v); return builder; } IDictionary dict = conditions.ToDictionary(); DynamicTypeMap mapper = DynamicMapperCache.GetMapper(conditions.GetType()); string table = dict.TryGetValue("_table").NullOr(x => x.ToString(), string.Empty); foreach (KeyValuePair condition in dict) { if (mapper.Ignored.Contains(condition.Key) || condition.Key == "_table") continue; string colName = mapper != null ? mapper.PropertyMap.TryGetValue(condition.Key) ?? condition.Key : condition.Key; DynamicSchemaColumn? col = null; // This should be used on typed queries or update/delete steatements, which usualy operate on a single table. if (schema) { col = builder.GetColumnFromSchema(colName, mapper, table); if ((!col.HasValue || !col.Value.IsKey) && (mapper == null || mapper.ColumnsMap.TryGetValue(colName).NullOr(m => m.Ignore || m.Column.NullOr(c => !c.IsKey, true), true))) continue; colName = col.HasValue ? col.Value.Name : colName; } if (!string.IsNullOrEmpty(table)) builder.InternalHaving(x => x(builder.FixObjectName(string.Format("{0}.{1}", table, colName))) == condition.Value); else builder.InternalHaving(x => x(builder.FixObjectName(colName)) == condition.Value); } return builder; } #endregion Where } internal static class DynamicModifyBuilderExtensions { internal static T Table(this T builder, Func func) where T : DynamicModifyBuilder { if (func == null) throw new ArgumentNullException("Function cannot be null."); using (DynamicParser parser = DynamicParser.Parse(func)) { object result = parser.Result; // If the expression result is string. if (result is string) return builder.Table((string)result); else if (result is Type) return builder.Table((Type)result); else if (result is DynamicParser.Node) { // Or if it resolves to a dynamic node DynamicParser.Node node = (DynamicParser.Node)result; string owner = null; string main = null; while (true) { // Deny support for the AS() virtual method... if (node is DynamicParser.Node.Method && ((DynamicParser.Node.Method)node).Name.ToUpper() == "AS") throw new ArgumentException(string.Format("Alias is not supported on modification builders. (Parsing: {0})", result)); // Support for table specifications... if (node is DynamicParser.Node.GetMember) { if (owner != null) throw new ArgumentException(string.Format("Owner '{0}.{1}' is already set when parsing '{2}'.", owner, main, result)); if (main != null) owner = ((DynamicParser.Node.GetMember)node).Name; else main = ((DynamicParser.Node.GetMember)node).Name; node = node.Host; continue; } // Support for generic sources... if (node is DynamicParser.Node.Invoke) { if (owner == null && main == null) { DynamicParser.Node.Invoke invoke = (DynamicParser.Node.Invoke)node; if (invoke.Arguments.Length == 1 && invoke.Arguments[0] is Type) return builder.Table((Type)invoke.Arguments[0]); else if (invoke.Arguments.Length == 1 && invoke.Arguments[0] is String) return builder.Table((string)invoke.Arguments[0]); else throw new ArgumentException(string.Format("Invalid argument count or type when parsing '{2}'. Invoke supports only one argument of type Type or String", owner, main, result)); } else if (owner != null) throw new ArgumentException(string.Format("Owner '{0}.{1}' is already set when parsing '{2}'.", owner, main, result)); else if (main != null) throw new ArgumentException(string.Format("Main '{0}' is already set when parsing '{1}'.", main, result)); } if (!string.IsNullOrEmpty(main)) return builder.Table(string.Format("{0}{1}", string.IsNullOrEmpty(owner) ? string.Empty : string.Format("{0}.", owner), main)); } } throw new ArgumentException(string.Format("Unable to set table parsing '{0}'", result)); } } internal static T Table(this T builder, string tableName, Dictionary schema = null) where T : DynamicModifyBuilder { Tuple tuple = tableName.Validated("Table Name").SplitSomethingAndAlias(); if (!string.IsNullOrEmpty(tuple.Item2)) throw new ArgumentException(string.Format("Can not use aliases in INSERT steatement. ({0})", tableName), "tableName"); string[] parts = tuple.Item1.Split('.'); if (parts.Length > 2) throw new ArgumentException(string.Format("Table name can consist only from name or owner and name. ({0})", tableName), "tableName"); builder.Tables.Clear(); builder.Tables.Add(new DynamicQueryBuilder.TableInfo(builder.Database, builder.Database.StripName(parts.Last()).Validated("Table"), null, parts.Length == 2 ? builder.Database.StripName(parts.First()).Validated("Owner", canbeNull: true) : null)); if (schema != null) (builder.Tables[0] as DynamicQueryBuilder.TableInfo).Schema = schema; return builder; } internal static T Table(this T builder, Type type) where T : DynamicQueryBuilder { if (type.IsAnonymous()) throw new InvalidOperationException(string.Format("Cant assign anonymous type as a table ({0}).", type.FullName)); DynamicTypeMap mapper = DynamicMapperCache.GetMapper(type); if (mapper == null) throw new InvalidOperationException("Cant assign unmapable type as a table."); if (builder is DynamicModifyBuilder) { builder.Tables.Clear(); builder.Tables.Add(new DynamicQueryBuilder.TableInfo(builder.Database, type)); } else if (builder is DynamicSelectQueryBuilder) (builder as DynamicSelectQueryBuilder).From(x => x(type)); return builder; } } internal static class DynamicWhereQueryExtensions { #region Where internal static T InternalWhere(this T builder, Func func) where T : DynamicQueryBuilder, DynamicQueryBuilder.IQueryWithWhere { return builder.InternalWhere(false, false, func); } internal static T InternalWhere(this T builder, bool addBeginBrace, bool addEndBrace, Func func) where T : DynamicQueryBuilder, DynamicQueryBuilder.IQueryWithWhere { if (func == null) throw new ArgumentNullException("Array of functions cannot be null."); using (DynamicParser parser = DynamicParser.Parse(func)) { string condition = null; bool and = true; object result = parser.Result; if (result is string) { condition = (string)result; if (condition.ToUpper().IndexOf("OR") == 0) { and = false; condition = condition.Substring(3); } else if (condition.ToUpper().IndexOf("AND") == 0) condition = condition.Substring(4); } else if (!(result is DynamicParser.Node) && !result.GetType().IsValueType) return builder.InternalWhere(result); else { // Intercepting the 'x => x.And()' and 'x => x.Or()' virtual methods... if (result is DynamicParser.Node.Method && ((DynamicParser.Node.Method)result).Host is DynamicParser.Node.Argument) { DynamicParser.Node.Method node = (DynamicParser.Node.Method)result; string name = node.Name.ToUpper(); if (name == "AND" || name == "OR") { object[] args = ((DynamicParser.Node.Method)node).Arguments; if (args == null) throw new ArgumentNullException("arg", string.Format("{0} is not a parameterless method.", name)); if (args.Length != 1) throw new ArgumentException(string.Format("{0} requires one and only one parameter: {1}.", name, args.Sketch())); and = name == "AND" ? true : false; result = args[0]; } } // Just parsing the contents now... condition = builder.Parse(result, pars: builder.Parameters).Validated("Where condition"); } if (addBeginBrace) builder.WhereOpenBracketsCount++; if (addEndBrace) builder.WhereOpenBracketsCount--; if (builder.WhereCondition == null) builder.WhereCondition = string.Format("{0}{1}{2}", addBeginBrace ? "(" : string.Empty, condition, addEndBrace ? ")" : string.Empty); else builder.WhereCondition = string.Format("{0} {1} {2}{3}{4}", builder.WhereCondition, and ? "AND" : "OR", addBeginBrace ? "(" : string.Empty, condition, addEndBrace ? ")" : string.Empty); } return builder; } internal static T InternalWhere(this T builder, DynamicColumn column) where T : DynamicQueryBuilder, DynamicQueryBuilder.IQueryWithWhere { bool virt = builder.VirtualMode; if (column.VirtualColumn.HasValue) builder.VirtualMode = column.VirtualColumn.Value; Action modParam = (p) => { if (column.Schema.HasValue) p.Schema = column.Schema; if (!p.Schema.HasValue) p.Schema = column.Schema ?? builder.GetColumnFromSchema(column.ColumnName); }; builder.CreateTemporaryParameterAction(modParam); // It's kind of uglu, but... well it works. if (column.Or) switch (column.Operator) { default: case DynamicColumn.CompareOperator.Eq: builder.InternalWhere(column.BeginBlock, column.EndBlock, x => x.Or(x(builder.FixObjectName(column.ColumnName)) == column.Value)); break; case DynamicColumn.CompareOperator.Not: builder.InternalWhere(column.BeginBlock, column.EndBlock, x => x.Or(x(builder.FixObjectName(column.ColumnName)) != column.Value)); break; case DynamicColumn.CompareOperator.Like: builder.InternalWhere(column.BeginBlock, column.EndBlock, x => x.Or(x(builder.FixObjectName(column.ColumnName)).Like(column.Value))); break; case DynamicColumn.CompareOperator.NotLike: builder.InternalWhere(column.BeginBlock, column.EndBlock, x => x.Or(x(builder.FixObjectName(column.ColumnName)).NotLike(column.Value))); break; case DynamicColumn.CompareOperator.In: builder.InternalWhere(column.BeginBlock, column.EndBlock, x => x.Or(x(builder.FixObjectName(column.ColumnName)).In(column.Value))); break; case DynamicColumn.CompareOperator.Lt: builder.InternalWhere(column.BeginBlock, column.EndBlock, x => x.Or(x(builder.FixObjectName(column.ColumnName)) < column.Value)); break; case DynamicColumn.CompareOperator.Lte: builder.InternalWhere(column.BeginBlock, column.EndBlock, x => x.Or(x(builder.FixObjectName(column.ColumnName)) <= column.Value)); break; case DynamicColumn.CompareOperator.Gt: builder.InternalWhere(column.BeginBlock, column.EndBlock, x => x.Or(x(builder.FixObjectName(column.ColumnName)) > column.Value)); break; case DynamicColumn.CompareOperator.Gte: builder.InternalWhere(column.BeginBlock, column.EndBlock, x => x.Or(x(builder.FixObjectName(column.ColumnName)) >= column.Value)); break; case DynamicColumn.CompareOperator.Between: builder.InternalWhere(column.BeginBlock, column.EndBlock, x => x.Or(x(builder.FixObjectName(column.ColumnName)).Between(column.Value))); break; } else switch (column.Operator) { default: case DynamicColumn.CompareOperator.Eq: builder.InternalWhere(column.BeginBlock, column.EndBlock, x => x(builder.FixObjectName(column.ColumnName)) == column.Value); break; case DynamicColumn.CompareOperator.Not: builder.InternalWhere(column.BeginBlock, column.EndBlock, x => x(builder.FixObjectName(column.ColumnName)) != column.Value); break; case DynamicColumn.CompareOperator.Like: builder.InternalWhere(column.BeginBlock, column.EndBlock, x => x(builder.FixObjectName(column.ColumnName)).Like(column.Value)); break; case DynamicColumn.CompareOperator.NotLike: builder.InternalWhere(column.BeginBlock, column.EndBlock, x => x(builder.FixObjectName(column.ColumnName)).NotLike(column.Value)); break; case DynamicColumn.CompareOperator.In: builder.InternalWhere(column.BeginBlock, column.EndBlock, x => x(builder.FixObjectName(column.ColumnName)).In(column.Value)); break; case DynamicColumn.CompareOperator.Lt: builder.InternalWhere(column.BeginBlock, column.EndBlock, x => x(builder.FixObjectName(column.ColumnName)) < column.Value); break; case DynamicColumn.CompareOperator.Lte: builder.InternalWhere(column.BeginBlock, column.EndBlock, x => x(builder.FixObjectName(column.ColumnName)) <= column.Value); break; case DynamicColumn.CompareOperator.Gt: builder.InternalWhere(column.BeginBlock, column.EndBlock, x => x(builder.FixObjectName(column.ColumnName)) > column.Value); break; case DynamicColumn.CompareOperator.Gte: builder.InternalWhere(column.BeginBlock, column.EndBlock, x => x(builder.FixObjectName(column.ColumnName)) >= column.Value); break; case DynamicColumn.CompareOperator.Between: builder.InternalWhere(column.BeginBlock, column.EndBlock, x => x(builder.FixObjectName(column.ColumnName)).Between(column.Value)); break; } builder.OnCreateTemporaryParameter.Remove(modParam); builder.VirtualMode = virt; return builder; } internal static T InternalWhere(this T builder, string column, DynamicColumn.CompareOperator op, object value) where T : DynamicQueryBuilder, DynamicQueryBuilder.IQueryWithWhere { if (value is DynamicColumn) { DynamicColumn v = (DynamicColumn)value; if (string.IsNullOrEmpty(v.ColumnName)) v.ColumnName = column; return builder.InternalWhere(v); } else if (value is IEnumerable) { foreach (DynamicColumn v in (IEnumerable)value) builder.InternalWhere(v); return builder; } return builder.InternalWhere(new DynamicColumn { ColumnName = column, Operator = op, Value = value }); } internal static T InternalWhere(this T builder, string column, object value) where T : DynamicQueryBuilder, DynamicQueryBuilder.IQueryWithWhere { return builder.InternalWhere(column, DynamicColumn.CompareOperator.Eq, value); } internal static T InternalWhere(this T builder, object conditions, bool schema = false) where T : DynamicQueryBuilder, DynamicQueryBuilder.IQueryWithWhere { if (conditions is DynamicColumn) return builder.InternalWhere((DynamicColumn)conditions); else if (conditions is IEnumerable) { foreach (DynamicColumn v in (IEnumerable)conditions) builder.InternalWhere(v); return builder; } IDictionary dict = conditions.ToDictionary(); DynamicTypeMap mapper = DynamicMapperCache.GetMapper(conditions.GetType()); string table = dict.TryGetValue("_table").NullOr(x => x.ToString(), string.Empty); foreach (KeyValuePair condition in dict) { if (mapper.Ignored.Contains(condition.Key) || condition.Key == "_table") continue; string colName = mapper != null ? mapper.PropertyMap.TryGetValue(condition.Key) ?? condition.Key : condition.Key; DynamicSchemaColumn? col = null; // This should be used on typed queries or update/delete steatements, which usualy operate on a single table. if (schema) { col = builder.GetColumnFromSchema(colName, mapper, table); if ((!col.HasValue || !col.Value.IsKey) && (mapper == null || mapper.ColumnsMap.TryGetValue(colName).NullOr(m => m.Ignore || m.Column.NullOr(c => !c.IsKey, true), true))) continue; colName = col.HasValue ? col.Value.Name : colName; } if (!string.IsNullOrEmpty(table)) builder.InternalWhere(x => x(builder.FixObjectName(string.Format("{0}.{1}", table, colName))) == condition.Value); else builder.InternalWhere(x => x(builder.FixObjectName(colName)) == condition.Value); } return builder; } #endregion Where } } namespace Implementation { /// Implementation of dynamic delete query builder. internal class DynamicDeleteQueryBuilder : DynamicModifyBuilder, IDynamicDeleteQueryBuilder, DynamicQueryBuilder.IQueryWithWhere { /// /// Initializes a new instance of the class. /// /// The database. internal DynamicDeleteQueryBuilder(DynamicDatabase db) : base(db) { } /// /// Initializes a new instance of the class. /// /// The database. /// Name of the table. public DynamicDeleteQueryBuilder(DynamicDatabase db, string tableName) : base(db, tableName) { } /// Generates the text this command will execute against the underlying database. /// The text to execute against the underlying database. /// This method must be override by derived classes. public override string CommandText() { ITableInfo info = Tables.Single(); return string.Format("DELETE FROM {0}{1}{2}{3}", string.IsNullOrEmpty(info.Owner) ? string.Empty : string.Format("{0}.", Database.DecorateName(info.Owner)), Database.DecorateName(info.Name), string.IsNullOrEmpty(WhereCondition) ? string.Empty : " WHERE ", WhereCondition); } #region Where /// /// Adds to the 'Where' clause the contents obtained from parsing the dynamic lambda expression given. The condition /// is parsed to the appropriate syntax, where the specific customs virtual methods supported by the parser are used /// as needed. /// - If several Where() methods are chained their contents are, by default, concatenated with an 'AND' operator. /// - The 'And()' and 'Or()' virtual method can be used to concatenate with an 'OR' or an 'AND' operator, as in: /// 'Where( x => x.Or( condition ) )'. /// /// The specification. /// This instance to permit chaining. public virtual IDynamicDeleteQueryBuilder Where(Func func) { return this.InternalWhere(func); } /// Add where condition. /// Condition column with operator and value. /// Builder instance. public virtual IDynamicDeleteQueryBuilder Where(DynamicColumn column) { return this.InternalWhere(column); } /// Add where condition. /// Condition column. /// Condition operator. /// Condition value. /// Builder instance. public virtual IDynamicDeleteQueryBuilder Where(string column, DynamicColumn.CompareOperator op, object value) { return this.InternalWhere(column, op, value); } /// Add where condition. /// Condition column. /// Condition value. /// Builder instance. public virtual IDynamicDeleteQueryBuilder Where(string column, object value) { return this.InternalWhere(column, value); } /// Add where condition. /// Set conditions as properties and values of an object. /// If true use schema to determine key columns and ignore those which /// aren't keys. /// Builder instance. public virtual IDynamicDeleteQueryBuilder Where(object conditions, bool schema = false) { return this.InternalWhere(conditions, schema); } #endregion Where } /// Implementation of dynamic insert query builder. internal class DynamicInsertQueryBuilder : DynamicModifyBuilder, IDynamicInsertQueryBuilder { protected string _columns; protected string _values; /// /// Initializes a new instance of the class. /// /// The database. internal DynamicInsertQueryBuilder(DynamicDatabase db) : base(db) { } /// /// Initializes a new instance of the class. /// /// The database. /// Name of the table. public DynamicInsertQueryBuilder(DynamicDatabase db, string tableName) : base(db, tableName) { } /// Generates the text this command will execute against the underlying database. /// The text to execute against the underlying database. /// This method must be override by derived classes. public override string CommandText() { ITableInfo info = Tables.Single(); return string.Format("INSERT INTO {0}{1} ({2}) VALUES ({3})", string.IsNullOrEmpty(info.Owner) ? string.Empty : string.Format("{0}.", Database.DecorateName(info.Owner)), Database.DecorateName(info.Name), _columns, _values); } #region Insert /// /// Specifies the columns to insert using the dynamic lambda expressions given. Each expression correspond to one /// column, and can: /// - Resolve to a string, in this case a '=' must appear in the string. /// - Resolve to a expression with the form: 'x => x.Column = Value'. /// /// The specifications. /// The specifications. /// This instance to permit chaining. public virtual IDynamicInsertQueryBuilder Values(Func fn, params Func[] func) { if (fn == null) throw new ArgumentNullException("Array of specifications cannot be null."); int index = InsertFunc(-1, fn); if (func != null) foreach (Func f in func) index = InsertFunc(index, f); return this; } private int InsertFunc(int index, Func f) { index++; if (f == null) throw new ArgumentNullException(string.Format("Specification #{0} cannot be null.", index)); using (DynamicParser parser = DynamicParser.Parse(f)) { object result = parser.Result; if (result == null) throw new ArgumentException(string.Format("Specification #{0} resolves to null.", index)); string main = null; string value = null; string str = null; // When 'x => x.Table.Column = value' or 'x => x.Column = value'... if (result is DynamicParser.Node.SetMember) { DynamicParser.Node.SetMember node = (DynamicParser.Node.SetMember)result; DynamicSchemaColumn? col = GetColumnFromSchema(node.Name); main = Database.DecorateName(node.Name); value = Parse(node.Value, ref col, pars: Parameters, nulls: true); _columns = _columns == null ? main : string.Format("{0}, {1}", _columns, main); _values = _values == null ? value : string.Format("{0}, {1}", _values, value); return index; } else if (!(result is DynamicParser.Node) && !result.GetType().IsValueType) { Insert(result); return index; } // Other specifications are considered invalid... string err = string.Format("Specification '{0}' is invalid.", result); str = Parse(result); if (str.Contains("=")) err += " May have you used a '==' instead of a '=' operator?"; throw new ArgumentException(err); } } /// Add insert fields. /// Insert column. /// Insert value. /// Builder instance. public virtual IDynamicInsertQueryBuilder Insert(string column, object value) { if (value is DynamicColumn) { DynamicColumn v = (DynamicColumn)value; if (string.IsNullOrEmpty(v.ColumnName)) v.ColumnName = column; return Insert(v); } return Insert(new DynamicColumn { ColumnName = column, Value = value, }); } /// Add insert fields. /// Set insert value as properties and values of an object. /// Builder instance. public virtual IDynamicInsertQueryBuilder Insert(object o) { if (o is DynamicColumn) { DynamicColumn column = (DynamicColumn)o; DynamicSchemaColumn? col = column.Schema ?? GetColumnFromSchema(column.ColumnName); string main = FixObjectName(column.ColumnName, onlyColumn: true); string value = Parse(column.Value, ref col, pars: Parameters, nulls: true); _columns = _columns == null ? main : string.Format("{0}, {1}", _columns, main); _values = _values == null ? value : string.Format("{0}, {1}", _values, value); return this; } IDictionary dict = o.ToDictionary(); DynamicTypeMap mapper = DynamicMapperCache.GetMapper(o.GetType()); if (mapper != null) { foreach (KeyValuePair con in dict) if (!mapper.Ignored.Contains(con.Key)) { string colName = mapper.PropertyMap.TryGetValue(con.Key) ?? con.Key; DynamicPropertyInvoker propMap = mapper.ColumnsMap.TryGetValue(colName.ToLower()); if (propMap == null || propMap.Column == null || !propMap.Column.IsNoInsert) Insert(colName, con.Value); // TODO: This probably should get value from mapper } } else foreach (KeyValuePair con in dict) Insert(con.Key, con.Value); return this; } #endregion Insert #region IExtendedDisposable /// Performs application-defined tasks associated with /// freeing, releasing, or resetting unmanaged resources. public override void Dispose() { base.Dispose(); _columns = _values = null; } #endregion IExtendedDisposable } /// Base query builder for insert/update/delete statements. internal abstract class DynamicModifyBuilder : DynamicQueryBuilder { /// /// Initializes a new instance of the class. /// /// The database. public DynamicModifyBuilder(DynamicDatabase db) : base(db) { VirtualMode = false; } /// /// Initializes a new instance of the class. /// /// The database. /// Name of the table. public DynamicModifyBuilder(DynamicDatabase db, string tableName) : this(db) { VirtualMode = false; this.Table(tableName); } /// Execute this builder. /// Result of an execution.. public virtual int Execute() { using (IDbConnection con = Database.Open()) using (IDbCommand cmd = con.CreateCommand()) { return cmd .SetCommand(this) .ExecuteNonQuery(); } } } /// Implementation of dynamic query builder base interface. internal abstract class DynamicQueryBuilder : IDynamicQueryBuilder { /// Empty interface to allow where query builder implementation use universal approach. internal interface IQueryWithWhere { /// Gets or sets the where condition. string WhereCondition { get; set; } /// Gets or sets the amount of not closed brackets in where statement. int WhereOpenBracketsCount { get; set; } } /// Empty interface to allow having query builder implementation use universal approach. internal interface IQueryWithHaving { /// Gets or sets the having condition. string HavingCondition { get; set; } /// Gets or sets the amount of not closed brackets in having statement. int HavingOpenBracketsCount { get; set; } } private DynamicQueryBuilder _parent = null; #region TableInfo /// Table information. internal class TableInfo : ITableInfo { /// /// Initializes a new instance of the class. /// internal TableInfo() { IsDisposed = false; } /// /// Initializes a new instance of the class. /// /// The database. /// The name of table. /// The table alias. /// The table owner. /// The table should be used with nolock. public TableInfo(DynamicDatabase db, string name, string alias = null, string owner = null, bool nolock = false) : this() { Name = name; Alias = alias; Owner = owner; NoLock = nolock; if (!name.ContainsAny(StringExtensions.InvalidMemberChars)) Schema = db.GetSchema(name, owner: owner); } /// /// Initializes a new instance of the class. /// /// The database. /// The type which can be mapped to database. /// The table alias. /// The table owner. /// The table should be used with nolock. public TableInfo(DynamicDatabase db, Type type, string alias = null, string owner = null, bool nolock = false) : this() { DynamicTypeMap mapper = DynamicMapperCache.GetMapper(type); Name = mapper.Table == null || string.IsNullOrEmpty(mapper.Table.Name) ? mapper.Type.Name : mapper.Table.Name; Owner = (mapper.Table != null) ? mapper.Table.Owner : owner; Alias = alias; NoLock = nolock; Schema = db.GetSchema(type); } /// Gets or sets table owner name. public string Owner { get; internal set; } /// Gets or sets table name. public string Name { get; internal set; } /// Gets or sets table alias. public string Alias { get; internal set; } /// Gets or sets table alias. public bool NoLock { get; internal set; } /// Gets or sets table schema. public Dictionary Schema { get; internal set; } /// Gets a value indicating whether this instance is disposed. public bool IsDisposed { get; private set; } /// Performs application-defined tasks associated with /// freeing, releasing, or resetting unmanaged resources. public virtual void Dispose() { IsDisposed = true; ////if (Schema != null) //// Schema.Clear(); Owner = Name = Alias = null; Schema = null; } } /// Generic based table information. /// Type of class that is represented in database. internal class TableInfo : TableInfo { /// /// Initializes a new instance of the class. /// /// The database. /// The table alias. /// The table owner. public TableInfo(DynamicDatabase db, string alias = null, string owner = null) : base(db, typeof(T), alias, owner) { } } #endregion TableInfo #region Parameter /// Interface describing parameter info. internal class Parameter : IParameter { /// Initializes a new instance of the /// class. public Parameter() { IsDisposed = false; } /// Gets or sets the parameter position in command. /// Available after filling the command. public int Ordinal { get; internal set; } /// Gets or sets the parameter temporary name. public string Name { get; internal set; } /// Gets or sets the parameter value. public object Value { get; set; } /// Gets or sets a value indicating whether name of temporary parameter is well known. public bool WellKnown { get; set; } /// Gets or sets a value indicating whether this is virtual. public bool Virtual { get; set; } /// Gets or sets the parameter schema information. public DynamicSchemaColumn? Schema { get; set; } /// Gets a value indicating whether this instance is disposed. public bool IsDisposed { get; private set; } /// Performs application-defined tasks associated with /// freeing, releasing, or resetting unmanaged resources. public virtual void Dispose() { IsDisposed = true; Name = null; Schema = null; } } #endregion Parameter #region Constructor /// /// Initializes a new instance of the class. /// /// The database. public DynamicQueryBuilder(DynamicDatabase db) { IsDisposed = false; VirtualMode = false; Tables = new List(); Parameters = new Dictionary(); OnCreateTemporaryParameter = new List>(); OnCreateParameter = new List>(); WhereCondition = null; WhereOpenBracketsCount = 0; Database = db; if (Database != null) Database.AddToCache(this); SupportSchema = (db.Options & DynamicDatabaseOptions.SupportSchema) == DynamicDatabaseOptions.SupportSchema; SupportNoLock = (db.Options & DynamicDatabaseOptions.SupportNoLock) == DynamicDatabaseOptions.SupportNoLock; } /// Initializes a new instance of the class. /// The database. /// The parent query. internal DynamicQueryBuilder(DynamicDatabase db, DynamicQueryBuilder parent) : this(db) { _parent = parent; } #endregion Constructor #region IQueryWithWhere /// Gets or sets the where condition. public string WhereCondition { get; set; } /// Gets or sets the amount of not closed brackets in where statement. public int WhereOpenBracketsCount { get; set; } #endregion IQueryWithWhere #region IDynamicQueryBuilder /// Gets instance. public DynamicDatabase Database { get; private set; } /// Gets the tables used in this builder. public IList Tables { get; private set; } /// Gets the tables used in this builder. public IDictionary Parameters { get; private set; } /// Gets or sets a value indicating whether add virtual parameters. public bool VirtualMode { get; set; } /// Gets or sets the on create temporary parameter actions. /// This is exposed to allow setting schema of column. public List> OnCreateTemporaryParameter { get; set; } /// Gets or sets the on create real parameter actions. /// This is exposed to allow modification of parameter. public List> OnCreateParameter { get; set; } /// Gets a value indicating whether database supports standard schema. public bool SupportSchema { get; private set; } /// Gets a value indicating whether database supports with no lock syntax. public bool SupportNoLock { get; private set; } /// /// Generates the text this command will execute against the underlying database. /// /// The text to execute against the underlying database. /// This method must be override by derived classes. public abstract string CommandText(); /// Fill command with query. /// Command to fill. /// Filled instance of . public virtual IDbCommand FillCommand(IDbCommand command) { // End not ended where statement if (this is IQueryWithWhere) { while (WhereOpenBracketsCount > 0) { WhereCondition += ")"; WhereOpenBracketsCount--; } } // End not ended having statement if (this is IQueryWithHaving) { IQueryWithHaving h = this as IQueryWithHaving; while (h.HavingOpenBracketsCount > 0) { h.HavingCondition += ")"; h.HavingOpenBracketsCount--; } } return command.SetCommand(CommandText() .FillStringWithVariables(s => { return Parameters.TryGetValue(s).NullOr(p => { IDbDataParameter param = (IDbDataParameter)command .AddParameter(this, p.Schema, p.Value) .Parameters[command.Parameters.Count - 1]; (p as Parameter).Ordinal = command.Parameters.Count - 1; if (OnCreateParameter != null) OnCreateParameter.ForEach(x => x(p, param)); return param.ParameterName; }, s); })); } #endregion IDynamicQueryBuilder #region Parser /// Parses the arbitrary object given and translates it into a string with the appropriate /// syntax for the database this parser is specific to. /// The object to parse and translate. It can be any arbitrary object, including null values (if /// permitted) and dynamic lambda expressions. /// If not null, the parameters' list where to store the parameters extracted by the parsing. /// If true, literal (raw) string are allowed. If false and the node is a literal then, as a /// security measure, an exception is thrown. /// True to accept null values and translate them into the appropriate syntax accepted by the /// database. If false and the value is null, then an exception is thrown. /// If set to true decorate element. /// If set parse argument as alias. This is workaround for AS method. /// A string containing the result of the parsing, along with the parameters extracted in the /// instance if such is given. /// Null nodes are not accepted. internal virtual string Parse(object node, IDictionary pars = null, bool rawstr = false, bool nulls = false, bool decorate = true, bool isMultiPart = true) { DynamicSchemaColumn? c = null; return Parse(node, ref c, pars, rawstr, nulls, decorate, isMultiPart); } /// Parses the arbitrary object given and translates it into a string with the appropriate /// syntax for the database this parser is specific to. /// The object to parse and translate. It can be any arbitrary object, including null values (if /// permitted) and dynamic lambda expressions. /// This parameter is used to determine type of parameter used in query. /// If not null, the parameters' list where to store the parameters extracted by the parsing. /// If true, literal (raw) string are allowed. If false and the node is a literal then, as a /// security measure, an exception is thrown. /// True to accept null values and translate them into the appropriate syntax accepted by the /// database. If false and the value is null, then an exception is thrown. /// If set to true decorate element. /// If set parse argument as alias. This is workaround for AS method. /// A string containing the result of the parsing, along with the parameters extracted in the /// instance if such is given. /// Null nodes are not accepted. internal virtual string Parse(object node, ref DynamicSchemaColumn? columnSchema, IDictionary pars = null, bool rawstr = false, bool nulls = false, bool decorate = true, bool isMultiPart = true) { // Null nodes are accepted or not depending upon the "nulls" flag... if (node == null) { if (!nulls) throw new ArgumentNullException("node", "Null nodes are not accepted."); return Dispatch(node, ref columnSchema, pars, decorate); } // Nodes that are strings are parametrized or not depending the "rawstr" flag... if (node is string) { if (rawstr) return (string)node; else return Dispatch(node, ref columnSchema, pars, decorate); } // If node is a delegate, parse it to create the logical tree... if (node is Delegate) { using (DynamicParser p = DynamicParser.Parse((Delegate)node)) { node = p.Result; return Parse(node, ref columnSchema, pars, rawstr, decorate: decorate); // Intercept containers as in (x => "string") } } return Dispatch(node, ref columnSchema, pars, decorate, isMultiPart); } private string Dispatch(object node, ref DynamicSchemaColumn? columnSchema, IDictionary pars = null, bool decorate = true, bool isMultiPart = true) { if (node != null) { if (node is DynamicQueryBuilder) return ParseCommand((DynamicQueryBuilder)node, pars); else if (node is DynamicParser.Node.Argument) return ParseArgument((DynamicParser.Node.Argument)node, isMultiPart); else if (node is DynamicParser.Node.GetMember) return ParseGetMember((DynamicParser.Node.GetMember)node, ref columnSchema, pars, decorate, isMultiPart); else if (node is DynamicParser.Node.SetMember) return ParseSetMember((DynamicParser.Node.SetMember)node, ref columnSchema, pars, decorate, isMultiPart); else if (node is DynamicParser.Node.Unary) return ParseUnary((DynamicParser.Node.Unary)node, pars); else if (node is DynamicParser.Node.Binary) return ParseBinary((DynamicParser.Node.Binary)node, pars); else if (node is DynamicParser.Node.Method) return ParseMethod((DynamicParser.Node.Method)node, ref columnSchema, pars); else if (node is DynamicParser.Node.Invoke) return ParseInvoke((DynamicParser.Node.Invoke)node, ref columnSchema, pars); else if (node is DynamicParser.Node.Convert) return ParseConvert((DynamicParser.Node.Convert)node, pars); } // All other cases are considered constant parameters... return ParseConstant(node, pars, columnSchema); } internal virtual string ParseCommand(DynamicQueryBuilder node, IDictionary pars = null) { // Getting the command's text... string str = node.CommandText(); // Avoiding spurious "OUTPUT XXX" statements // If there are parameters to transform, but cannot store them, it is an error if (node.Parameters.Count != 0 && pars == null) return string.Format("({0})", str); // TODO: Make special condiion ////throw new InvalidOperationException(string.Format("The parameters in this command '{0}' cannot be added to a null collection.", node.Parameters)); // Copy parameters to new comand foreach (KeyValuePair parameter in node.Parameters) pars.Add(parameter.Key, parameter.Value); return string.Format("({0})", str); } protected virtual string ParseArgument(DynamicParser.Node.Argument node, bool isMultiPart = true, bool isOwner = false) { if (!string.IsNullOrEmpty(node.Name) && (isOwner || (isMultiPart && IsTableAlias(node.Name)))) return node.Name; return null; } protected virtual string ParseGetMember(DynamicParser.Node.GetMember node, ref DynamicSchemaColumn? columnSchema, IDictionary pars = null, bool decorate = true, bool isMultiPart = true) { if (node.Host is DynamicParser.Node.Argument && IsTableAlias(node.Name)) { decorate = false; isMultiPart = false; } // This hack allows to use argument as alias, but when it is not nesesary use other column. // Let say we hace a table Users with alias usr, and we join to table with alias ua which also has a column Users // This allow use of usr => usr.ua.Users to result in ua."Users" instead of "Users" or usr."ua"."Users", se tests for examples. string parent = null; if (node.Host != null) { if (isMultiPart && node.Host is DynamicParser.Node.GetMember && IsTable(node.Host.Name, null)) { if (node.Host.Host != null && node.Host.Host is DynamicParser.Node.GetMember && IsTable(node.Host.Name, node.Host.Host.Name)) parent = string.Format("{0}.{1}", Parse(node.Host.Host, pars, isMultiPart: false), Parse(node.Host, pars, isMultiPart: false)); else parent = Parse(node.Host, pars, isMultiPart: false); } else if (isMultiPart) parent = Parse(node.Host, pars, isMultiPart: isMultiPart); } ////string parent = node.Host == null || !isMultiPart ? null : Parse(node.Host, pars, isMultiPart: !IsTable(node.Name, node.Host.Name)); string name = parent == null ? decorate ? Database.DecorateName(node.Name) : node.Name : string.Format("{0}.{1}", parent, decorate ? Database.DecorateName(node.Name) : node.Name); columnSchema = GetColumnFromSchema(name); return name; } protected virtual string ParseSetMember(DynamicParser.Node.SetMember node, ref DynamicSchemaColumn? columnSchema, IDictionary pars = null, bool decorate = true, bool isMultiPart = true) { if (node.Host is DynamicParser.Node.Argument && IsTableAlias(node.Name)) { decorate = false; isMultiPart = false; } string parent = null; if (node.Host != null) { if (isMultiPart && node.Host is DynamicParser.Node.GetMember && IsTable(node.Host.Name, null)) { if (node.Host.Host != null && node.Host.Host is DynamicParser.Node.GetMember && IsTable(node.Name, node.Host.Name)) parent = string.Format("{0}.{1}", Parse(node.Host.Host, pars, isMultiPart: false), Parse(node.Host, pars, isMultiPart: false)); else parent = Parse(node.Host, pars, isMultiPart: false); } else if (isMultiPart) parent = Parse(node.Host, pars, isMultiPart: isMultiPart); } ////string parent = node.Host == null || !isMultiPart ? null : Parse(node.Host, pars, isMultiPart: !IsTable(node.Name, node.Host.Name)); string name = parent == null ? decorate ? Database.DecorateName(node.Name) : node.Name : string.Format("{0}.{1}", parent, decorate ? Database.DecorateName(node.Name) : node.Name); columnSchema = GetColumnFromSchema(name); string value = Parse(node.Value, ref columnSchema, pars, nulls: true); return string.Format("{0} = ({1})", name, value); } protected virtual string ParseUnary(DynamicParser.Node.Unary node, IDictionary pars = null) { switch (node.Operation) { // Artifacts from the DynamicParser class that are not usefull here... case ExpressionType.IsFalse: case ExpressionType.IsTrue: return Parse(node.Target, pars); // Unary supported operations... case ExpressionType.Not: return string.Format("(NOT {0})", Parse(node.Target, pars)); case ExpressionType.Negate: return string.Format("!({0})", Parse(node.Target, pars)); } throw new ArgumentException("Not supported unary operation: " + node); } protected virtual string ParseBinary(DynamicParser.Node.Binary node, IDictionary pars = null) { string op = string.Empty; switch (node.Operation) { // Arithmetic binary operations... case ExpressionType.Add: op = "+"; break; case ExpressionType.Subtract: op = "-"; break; case ExpressionType.Multiply: op = "*"; break; case ExpressionType.Divide: op = "/"; break; case ExpressionType.Modulo: op = "%"; break; case ExpressionType.Power: op = "^"; break; case ExpressionType.And: op = "AND"; break; case ExpressionType.Or: op = "OR"; break; // Logical comparisons... case ExpressionType.GreaterThan: op = ">"; break; case ExpressionType.GreaterThanOrEqual: op = ">="; break; case ExpressionType.LessThan: op = "<"; break; case ExpressionType.LessThanOrEqual: op = "<="; break; // Comparisons against 'NULL' require the 'IS' or 'IS NOT' operator instead the numeric ones... case ExpressionType.Equal: op = node.Right == null && !VirtualMode ? "IS" : "="; break; case ExpressionType.NotEqual: op = node.Right == null && !VirtualMode ? "IS NOT" : "<>"; break; default: throw new ArgumentException("Not supported operator: '" + node.Operation); } DynamicSchemaColumn? columnSchema = null; string left = Parse(node.Left, ref columnSchema, pars); // Not nulls: left is assumed to be an object string right = Parse(node.Right, ref columnSchema, pars, nulls: true); return string.Format("({0} {1} {2})", left, op, right); } protected virtual string ParseMethod(DynamicParser.Node.Method node, ref DynamicSchemaColumn? columnSchema, IDictionary pars = null) { string method = node.Name.ToUpper(); string parent = node.Host == null ? null : Parse(node.Host, ref columnSchema, pars: pars); string item = null; // Root-level methods... if (node.Host == null) { switch (method) { case "NOT": if (node.Arguments == null || node.Arguments.Length != 1) throw new ArgumentNullException("NOT method expects one argument: " + node.Arguments.Sketch()); item = Parse(node.Arguments[0], ref columnSchema, pars: pars); return string.Format("(NOT {0})", item); } } // Column-level methods... if (node.Host != null) { switch (method) { case "BETWEEN": { if (node.Arguments == null || node.Arguments.Length == 0) throw new ArgumentException("BETWEEN method expects at least one argument: " + node.Arguments.Sketch()); if (node.Arguments.Length > 2) throw new ArgumentException("BETWEEN method expects at most two arguments: " + node.Arguments.Sketch()); object[] arguments = node.Arguments; if (arguments.Length == 1 && (arguments[0] is IEnumerable || arguments[0] is Array) && !(arguments[0] is byte[])) { IEnumerable vals = arguments[0] as IEnumerable; if (vals == null && arguments[0] is Array) vals = ((Array)arguments[0]).Cast() as IEnumerable; if (vals != null) arguments = vals.ToArray(); else throw new ArgumentException("BETWEEN method expects single argument to be enumerable of exactly two elements: " + node.Arguments.Sketch()); } return string.Format("{0} BETWEEN {1} AND {2}", parent, Parse(arguments[0], ref columnSchema, pars: pars), Parse(arguments[1], ref columnSchema, pars: pars)); } case "IN": { if (node.Arguments == null || node.Arguments.Length == 0) throw new ArgumentException("IN method expects at least one argument: " + node.Arguments.Sketch()); bool firstParam = true; StringBuilder sbin = new StringBuilder(); foreach (object arg in node.Arguments) { if (!firstParam) sbin.Append(", "); if ((arg is IEnumerable || arg is Array) && !(arg is byte[])) { IEnumerable vals = arg as IEnumerable; if (vals == null && arg is Array) vals = ((Array)arg).Cast() as IEnumerable; if (vals != null) foreach (object val in vals) { if (!firstParam) sbin.Append(", "); else firstParam = false; sbin.Append(Parse(val, ref columnSchema, pars: pars)); } else sbin.Append(Parse(arg, ref columnSchema, pars: pars)); } else sbin.Append(Parse(arg, ref columnSchema, pars: pars)); firstParam = false; } return string.Format("{0} IN({1})", parent, sbin.ToString()); } case "NOTIN": { if (node.Arguments == null || node.Arguments.Length == 0) throw new ArgumentException("IN method expects at least one argument: " + node.Arguments.Sketch()); bool firstParam = true; StringBuilder sbin = new StringBuilder(); foreach (object arg in node.Arguments) { if (!firstParam) sbin.Append(", "); if ((arg is IEnumerable || arg is Array) && !(arg is byte[])) { IEnumerable vals = arg as IEnumerable; if (vals == null && arg is Array) vals = ((Array)arg).Cast() as IEnumerable; if (vals != null) foreach (object val in vals) { if (!firstParam) sbin.Append(", "); else firstParam = false; sbin.Append(Parse(val, ref columnSchema, pars: pars)); } else sbin.Append(Parse(arg, ref columnSchema, pars: pars)); } else sbin.Append(Parse(arg, ref columnSchema, pars: pars)); firstParam = false; } return string.Format("{0} NOT IN({1})", parent, sbin.ToString()); } case "LIKE": if (node.Arguments == null || node.Arguments.Length != 1) throw new ArgumentException("LIKE method expects one argument: " + node.Arguments.Sketch()); return string.Format("{0} LIKE {1}", parent, Parse(node.Arguments[0], ref columnSchema, pars: pars)); case "NOTLIKE": if (node.Arguments == null || node.Arguments.Length != 1) throw new ArgumentException("NOT LIKE method expects one argument: " + node.Arguments.Sketch()); return string.Format("{0} NOT LIKE {1}", parent, Parse(node.Arguments[0], ref columnSchema, pars: pars)); case "AS": if (node.Arguments == null || node.Arguments.Length != 1) throw new ArgumentException("AS method expects one argument: " + node.Arguments.Sketch()); item = Parse(node.Arguments[0], pars: null, rawstr: true, isMultiPart: false); // pars=null to avoid to parameterize aliases item = item.Validated("Alias"); // Intercepting null and empty aliases return string.Format("{0} AS {1}", parent, item); case "NOLOCK": if (!SupportNoLock) return parent; if (node.Arguments != null && node.Arguments.Length > 1) throw new ArgumentException("NOLOCK method expects no arguments."); return string.Format("{0} {1}", parent, "WITH(NOLOCK)"); case "COUNT": if (node.Arguments != null && node.Arguments.Length > 1) throw new ArgumentException("COUNT method expects one or none argument: " + node.Arguments.Sketch()); if (node.Arguments == null || node.Arguments.Length == 0) return "COUNT(*)"; return string.Format("COUNT({0})", Parse(node.Arguments[0], ref columnSchema, pars: Parameters, nulls: true)); case "COUNT0": if (node.Arguments != null && node.Arguments.Length > 0) throw new ArgumentException("COUNT0 method doesn't expect arguments"); return "COUNT(0)"; } } // Default case: parsing the method's name along with its arguments... method = parent == null ? node.Name : string.Format("{0}.{1}", parent, node.Name); StringBuilder sb = new StringBuilder(); sb.AppendFormat("{0}(", method); if (node.Arguments != null && node.Arguments.Length != 0) { bool first = true; foreach (object argument in node.Arguments) { if (!first) sb.Append(", "); else first = false; sb.Append(Parse(argument, ref columnSchema, pars, nulls: true)); // We don't accept raw strings here!!! } } sb.Append(")"); return sb.ToString(); } protected virtual string ParseInvoke(DynamicParser.Node.Invoke node, ref DynamicSchemaColumn? columnSchema, IDictionary pars = null) { // This is used as an especial syntax to merely concatenate its arguments. It is used as a way to extend the supported syntax without the need of treating all the possible cases... if (node.Arguments == null || node.Arguments.Length == 0) return string.Empty; StringBuilder sb = new StringBuilder(); foreach (object arg in node.Arguments) { if (arg is string) { sb.Append((string)arg); if (node.Arguments.Length == 1 && !columnSchema.HasValue) columnSchema = GetColumnFromSchema((string)arg); } else sb.Append(Parse(arg, ref columnSchema, pars, rawstr: true, nulls: true)); } return sb.ToString(); } protected virtual string ParseConvert(DynamicParser.Node.Convert node, IDictionary pars = null) { // The cast mechanism is left for the specific database implementation, that should override this method // as needed... string r = Parse(node.Target, pars); return r; } protected virtual string ParseConstant(object node, IDictionary pars = null, DynamicSchemaColumn? columnSchema = null) { if (node == null && !VirtualMode) return ParseNull(); if (pars != null) { bool wellKnownName = VirtualMode && node is String && ((String)node).StartsWith("[$") && ((String)node).EndsWith("]") && ((String)node).Length > 4; // If we have a list of parameters to store it, let's parametrize it Parameter par = new Parameter() { Name = wellKnownName ? ((String)node).Substring(2, ((String)node).Length - 3) : Guid.NewGuid().ToString(), Value = wellKnownName ? null : node, WellKnown = wellKnownName, Virtual = VirtualMode, Schema = columnSchema, }; // If we are adding parameter we inform external sources about this. if (OnCreateTemporaryParameter != null) OnCreateTemporaryParameter.ForEach(x => x(par)); pars.Add(par.Name, par); return string.Format("[${0}]", par.Name); } return node.ToString(); // Last resort case } protected virtual string ParseNull() { return "NULL"; // Override if needed } #endregion Parser #region Helpers internal bool IsTableAlias(string name) { DynamicQueryBuilder builder = this; while (builder != null) { if (builder.Tables.Any(t => t.Alias == name)) return true; builder = builder._parent; } return false; } internal bool IsTable(string name, string owner) { DynamicQueryBuilder builder = this; while (builder != null) { if ((string.IsNullOrEmpty(owner) && builder.Tables.Any(t => t.Name.ToLower() == name.ToLower())) || (!string.IsNullOrEmpty(owner) && builder.Tables.Any(t => t.Name.ToLower() == name.ToLower() && !string.IsNullOrEmpty(t.Owner) && t.Owner.ToLower() == owner.ToLower()))) return true; builder = builder._parent; } return false; } internal string FixObjectName(string main, bool onlyColumn = false) { if (main.IndexOf("(") > 0 && main.IndexOf(")") > 0) return main.FillStringWithVariables(f => string.Format("({0})", FixObjectNamePrivate(f, onlyColumn)), "(", ")"); else return FixObjectNamePrivate(main, onlyColumn); } private string FixObjectNamePrivate(string f, bool onlyColumn = false) { IEnumerable objects = f.Split('.') .Select(x => Database.StripName(x)); if (onlyColumn || objects.Count() == 1) f = Database.DecorateName(objects.Last()); else if (!IsTableAlias(objects.First())) f = string.Join(".", objects.Select(o => Database.DecorateName(o))); else f = string.Format("{0}.{1}", objects.First(), string.Join(".", objects.Skip(1).Select(o => Database.DecorateName(o)))); return f; } internal DynamicSchemaColumn? GetColumnFromSchema(string colName, DynamicTypeMap mapper = null, string table = null) { // This is tricky and will not always work unfortunetly. ////if (colName.ContainsAny(StringExtensions.InvalidMultipartMemberChars)) //// return null; // First we need to get real column name and it's owner if exist. string[] parts = colName.Split('.'); for (int i = 0; i < parts.Length; i++) parts[i] = Database.StripName(parts[i]); string columnName = parts.Last(); // Get table name from mapper string tableName = table; if (string.IsNullOrEmpty(tableName)) { tableName = (mapper != null && mapper.Table != null) ? mapper.Table.Name : string.Empty; if (parts.Length > 1 && string.IsNullOrEmpty(tableName)) { // OK, we have a multi part identifier, that's good, we can get table name tableName = string.Join(".", parts.Take(parts.Length - 1)); } } // Try to get table info from cache ITableInfo tableInfo = !string.IsNullOrEmpty(tableName) ? Tables.FirstOrDefault(x => !string.IsNullOrEmpty(x.Alias) && x.Alias.ToLower() == tableName) ?? Tables.FirstOrDefault(x => x.Name.ToLower() == tableName.ToLower()) ?? Tables.FirstOrDefault() : this is DynamicModifyBuilder || Tables.Count == 1 ? Tables.FirstOrDefault() : null; // Try to get column from schema if (tableInfo != null && tableInfo.Schema != null) return tableInfo.Schema.TryGetNullable(columnName.ToLower()); // Well, we failed to find a column return null; } #endregion Helpers #region IExtendedDisposable /// Gets a value indicating whether this instance is disposed. public bool IsDisposed { get; private set; } /// Performs application-defined tasks associated with /// freeing, releasing, or resetting unmanaged resources. public virtual void Dispose() { if (IsDisposed) return; IsDisposed = true; if (Database != null) Database.RemoveFromCache(this); if (Parameters != null) { foreach (KeyValuePair p in Parameters) p.Value.Dispose(); Parameters.Clear(); Parameters = null; } if (Tables != null) { foreach (ITableInfo t in Tables) if (t != null) t.Dispose(); Tables.Clear(); Tables = null; } WhereCondition = null; Database = null; } #endregion IExtendedDisposable } /// Implementation of dynamic select query builder. internal class DynamicSelectQueryBuilder : DynamicQueryBuilder, IDynamicSelectQueryBuilder, DynamicQueryBuilder.IQueryWithWhere, DynamicQueryBuilder.IQueryWithHaving { private int? _limit = null; private int? _offset = null; private bool _distinct = false; protected string _select; private string _from; protected string _join; protected string _groupby; protected string _orderby; #region IQueryWithHaving /// Gets or sets the having condition. public string HavingCondition { get; set; } /// Gets or sets the amount of not closed brackets in having statement. public int HavingOpenBracketsCount { get; set; } #endregion IQueryWithHaving /// /// Gets a value indicating whether this instance has select columns. /// public bool HasSelectColumns { get { return !string.IsNullOrEmpty(_select); } } /// /// Initializes a new instance of the class. /// /// The database. public DynamicSelectQueryBuilder(DynamicDatabase db) : base(db) { } /// /// Initializes a new instance of the class. /// /// The database. /// The parent query. internal DynamicSelectQueryBuilder(DynamicDatabase db, DynamicQueryBuilder parent) : base(db, parent) { } /// Generates the text this command will execute against the underlying database. /// The text to execute against the underlying database. public override string CommandText() { bool lused = false; bool oused = false; StringBuilder sb = new StringBuilder("SELECT"); if (_distinct) sb.AppendFormat(" DISTINCT"); if (_limit.HasValue) { if ((Database.Options & DynamicDatabaseOptions.SupportTop) == DynamicDatabaseOptions.SupportTop) { sb.AppendFormat(" TOP {0}", _limit); lused = true; } else if ((Database.Options & DynamicDatabaseOptions.SupportFirstSkip) == DynamicDatabaseOptions.SupportFirstSkip) { sb.AppendFormat(" FIRST {0}", _limit); lused = true; } } if (_offset.HasValue && (Database.Options & DynamicDatabaseOptions.SupportFirstSkip) == DynamicDatabaseOptions.SupportFirstSkip) { sb.AppendFormat(" SKIP {0}", _offset); oused = true; } if (_select != null) sb.AppendFormat(" {0}", _select); else sb.Append(" *"); if (_from != null) sb.AppendFormat(" FROM {0}", _from); if (_join != null) sb.AppendFormat(" {0}", _join); if (WhereCondition != null) sb.AppendFormat(" WHERE {0}", WhereCondition); if (_groupby != null) sb.AppendFormat(" GROUP BY {0}", _groupby); if (HavingCondition != null) sb.AppendFormat(" HAVING {0}", HavingCondition); if (_orderby != null) sb.AppendFormat(" ORDER BY {0}", _orderby); if (_limit.HasValue && !lused && (Database.Options & DynamicDatabaseOptions.SupportLimitOffset) == DynamicDatabaseOptions.SupportLimitOffset) sb.AppendFormat(" LIMIT {0}", _limit); if (_offset.HasValue && !oused && (Database.Options & DynamicDatabaseOptions.SupportLimitOffset) == DynamicDatabaseOptions.SupportLimitOffset) sb.AppendFormat(" OFFSET {0}", _offset); return sb.ToString(); } #region Execution /// Execute this builder. /// Enumerator of objects expanded from query. public virtual IEnumerable Execute() { using (IDbConnection con = Database.Open()) using (IDbCommand cmd = con.CreateCommand()) { using (IDataReader rdr = cmd .SetCommand(this) .ExecuteReader()) using (IDataReader cache = new DynamicCachedReader(rdr)) while (cache.Read()) { dynamic val = null; // Work around to avoid yield being in try...catchblock: // 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; } } } /// Execute this builder and map to given type. /// Type of object to map on. /// Enumerator of objects expanded from query. public virtual IEnumerable Execute() where T : class { DynamicTypeMap mapper = DynamicMapperCache.GetMapper(); if (mapper == null) throw new InvalidOperationException("Type can't be mapped for unknown reason."); using (IDbConnection con = Database.Open()) using (IDbCommand cmd = con.CreateCommand()) { using (IDataReader rdr = cmd .SetCommand(this) .ExecuteReader()) using (IDataReader cache = new DynamicCachedReader(rdr)) while (cache.Read()) { dynamic val = null; // Work around to avoid yield being in try...catchblock: // 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 mapper.Create(val) as T; } } } /// Execute this builder as a data reader. /// Action containing reader. public virtual void ExecuteDataReader(Action reader) { using (IDbConnection con = Database.Open()) using (IDbCommand cmd = con.CreateCommand()) using (IDataReader rdr = cmd .SetCommand(this) .ExecuteReader()) reader(rdr); } /// Execute this builder as a data reader, but /// first makes a full reader copy in memory. /// Action containing reader. public virtual void ExecuteCachedDataReader(Action reader) { using (IDbConnection con = Database.Open()) using (IDbCommand cmd = con.CreateCommand()) using (IDataReader rdr = cmd .SetCommand(this) .ExecuteReader()) using (IDataReader cache = new DynamicCachedReader(rdr)) reader(cache); } /// Returns a single result. /// Result of a query. public virtual object Scalar() { using (IDbConnection con = Database.Open()) using (IDbCommand cmd = con.CreateCommand()) { return cmd .SetCommand(this) .ExecuteScalar(); } } #if !DYNAMORM_OMMIT_GENERICEXECUTION && !DYNAMORM_OMMIT_TRYPARSE /// Returns a single result. /// Type to parse to. /// Default value. /// Result of a query. public virtual T ScalarAs(T defaultValue = default(T)) { using (IDbConnection con = Database.Open()) using (IDbCommand cmd = con.CreateCommand()) { return cmd .SetCommand(this) .ExecuteScalarAs(defaultValue); } } #endif #endregion Execution #region From/Join /// /// Adds to the 'From' clause the contents obtained by parsing the dynamic lambda expressions given. The supported /// formats are: /// - Resolve to a string: 'x => "Table AS Alias', where the alias part is optional. /// - Resolve to an expression: 'x => x.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. /// The specification. /// This instance to permit chaining. public virtual IDynamicSelectQueryBuilder From(Func fn, params Func[] func) { if (fn == null) throw new ArgumentNullException("Array of functions cannot be or contain null."); int index = FromFunc(-1, fn); foreach (Func f in func) index = FromFunc(index, f); return this; } private int FromFunc(int index, Func f) { if (f == null) throw new ArgumentNullException("Array of functions cannot be or contain null."); index++; ITableInfo tableInfo = null; using (DynamicParser parser = DynamicParser.Parse(f)) { object result = parser.Result; // If the expression result is string. if (result is string) { string node = (string)result; Tuple tuple = node.SplitSomethingAndAlias(); string[] parts = tuple.Item1.Split('.'); tableInfo = new TableInfo(Database, Database.StripName(parts.Last()).Validated("Table"), tuple.Item2.Validated("Alias", canbeNull: true), parts.Length == 2 ? Database.StripName(parts.First()).Validated("Owner", canbeNull: true) : null); } else if (result is Type) { Type type = (Type)result; if (type.IsAnonymous()) throw new InvalidOperationException(string.Format("Cant assign anonymous type as a table ({0}). Parsing {1}", type.FullName, result)); DynamicTypeMap mapper = DynamicMapperCache.GetMapper(type); if (mapper == null) throw new InvalidOperationException(string.Format("Cant assign unmapable type as a table ({0}). Parsing {1}", type.FullName, result)); tableInfo = new TableInfo(Database, type); } else if (result is DynamicParser.Node) { // Or if it resolves to a dynamic node DynamicParser.Node node = (DynamicParser.Node)result; string owner = null; string main = null; string alias = null; bool nolock = false; Type type = null; while (true) { // Support for the AS() virtual method... if (node is DynamicParser.Node.Method && ((DynamicParser.Node.Method)node).Name.ToUpper() == "AS") { if (alias != null) throw new ArgumentException(string.Format("Alias '{0}' is already set when parsing '{1}'.", alias, result)); object[] args = ((DynamicParser.Node.Method)node).Arguments; if (args == null) throw new ArgumentNullException("arg", "AS() is not a parameterless method."); if (args.Length != 1) throw new ArgumentException("AS() requires one and only one parameter: " + args.Sketch()); alias = Parse(args[0], rawstr: true, decorate: false).Validated("Alias"); node = node.Host; continue; } // Support for the NoLock() virtual method... if (node is DynamicParser.Node.Method && ((DynamicParser.Node.Method)node).Name.ToUpper() == "NOLOCK") { object[] args = ((DynamicParser.Node.Method)node).Arguments; if (args != null && args.Length > 0) throw new ArgumentNullException("arg", "NoLock() doesn't support arguments."); nolock = true; node = node.Host; continue; } /*if (node is DynamicParser.Node.Method && ((DynamicParser.Node.Method)node).Name.ToUpper() == "subquery") { main = Parse(this.SubQuery(((DynamicParser.Node.Method)node).Arguments.Where(p => p is Func).Cast>().ToArray()), Parameters); continue; }*/ // Support for table specifications... if (node is DynamicParser.Node.GetMember) { if (owner != null) throw new ArgumentException(string.Format("Owner '{0}.{1}' is already set when parsing '{2}'.", owner, main, result)); if (main != null) owner = ((DynamicParser.Node.GetMember)node).Name; else main = ((DynamicParser.Node.GetMember)node).Name; node = node.Host; continue; } // Support for generic sources... if (node is DynamicParser.Node.Invoke) { if (owner != null) throw new ArgumentException(string.Format("Owner '{0}.{1}' is already set when parsing '{2}'.", owner, main, result)); if (main != null) owner = string.Format("{0}", Parse(node, rawstr: true, pars: Parameters)); else { DynamicParser.Node.Invoke invoke = (DynamicParser.Node.Invoke)node; if (invoke.Arguments.Length == 1 && invoke.Arguments[0] is Type) { type = (Type)invoke.Arguments[0]; if (type.IsAnonymous()) throw new InvalidOperationException(string.Format("Cant assign anonymous type as a table ({0}). Parsing {1}", type.FullName, result)); DynamicTypeMap mapper = DynamicMapperCache.GetMapper(type); if (mapper == null) throw new InvalidOperationException(string.Format("Cant assign unmapable type as a table ({0}). Parsing {1}", type.FullName, result)); main = mapper.Table == null || string.IsNullOrEmpty(mapper.Table.Name) ? mapper.Type.Name : mapper.Table.Name; owner = (mapper.Table != null) ? mapper.Table.Owner : owner; } else main = string.Format("{0}", Parse(node, rawstr: true, pars: Parameters)); } node = node.Host; continue; } // Just finished the parsing... if (node is DynamicParser.Node.Argument) break; // All others are assumed to be part of the main element... if (main != null) main = Parse(node, pars: Parameters); else main = Parse(node, pars: Parameters); break; } if (!string.IsNullOrEmpty(main)) tableInfo = type == null ? new TableInfo(Database, main, alias, owner, nolock) : new TableInfo(Database, type, alias, owner, nolock); else throw new ArgumentException(string.Format("Specification #{0} is invalid: {1}", index, result)); } // Or it is a not supported expression... if (tableInfo == null) throw new ArgumentException(string.Format("Specification #{0} is invalid: {1}", index, result)); Tables.Add(tableInfo); // We finally add the contents... StringBuilder sb = new StringBuilder(); if (!string.IsNullOrEmpty(tableInfo.Owner)) sb.AppendFormat("{0}.", Database.DecorateName(tableInfo.Owner)); sb.Append(tableInfo.Name.ContainsAny(StringExtensions.InvalidMemberChars) ? tableInfo.Name : Database.DecorateName(tableInfo.Name)); if (!string.IsNullOrEmpty(tableInfo.Alias)) sb.AppendFormat(" AS {0}", tableInfo.Alias); if (SupportNoLock && tableInfo.NoLock) sb.AppendFormat(" WITH(NOLOCK)"); _from = string.IsNullOrEmpty(_from) ? sb.ToString() : string.Format("{0}, {1}", _from, sb.ToString()); } return index; } /// /// Adds to the 'Join' clause the contents obtained by parsing the dynamic lambda expressions given. The supported /// formats are: /// - Resolve to a string: 'x => "Table AS Alias ON Condition', where the alias part is optional. /// - Resolve to an expression: 'x => x.Table.As( x.Alias ).On( condition )', where the alias part is optional. /// - Generic expression: 'x => x( expression ).As( x.Alias ).On( condition )', where the alias part is mandatory. /// In this case the alias is not annotated. /// The expression might be prepended by a method that, in this case, is used to specify the specific join type you /// want to perform, as in: 'x => x.Left()...". Two considerations apply: /// - If a 'false' argument is used when no 'Join' part appears in its name, then no 'Join' suffix is added /// with a space in between. /// - If a 'false' argument is used when a 'Join' part does appear, then no split is performed to separate the /// 'Join' part. /// /// The specification. /// This instance to permit chaining. public virtual IDynamicSelectQueryBuilder Join(params Func[] func) { // We need to do two passes to add aliases first. return JoinInternal(true, func).JoinInternal(false, func); } /// /// Adds to the 'Join' clause the contents obtained by parsing the dynamic lambda expressions given. The supported /// formats are: /// - Resolve to a string: 'x => "Table AS Alias ON Condition', where the alias part is optional. /// - Resolve to an expression: 'x => x.Table.As( x.Alias ).On( condition )', where the alias part is optional. /// - Generic expression: 'x => x( expression ).As( x.Alias ).On( condition )', where the alias part is mandatory. /// In this case the alias is not annotated. /// The expression might be prepended by a method that, in this case, is used to specify the specific join type you /// want to perform, as in: 'x => x.Left()...". Two considerations apply: /// - If a 'false' argument is used when no 'Join' part appears in its name, then no 'Join' suffix is added /// with a space in between. /// - If a 'false' argument is used when a 'Join' part does appear, then no split is performed to separate the /// 'Join' part. /// /// If true just pass by to locate tables and aliases, otherwise create rules. /// The specification. /// This instance to permit chaining. protected virtual DynamicSelectQueryBuilder JoinInternal(bool justAddTables, params Func[] func) { if (func == null) throw new ArgumentNullException("Array of functions cannot be null."); int index = -1; foreach (Func f in func) { index++; ITableInfo tableInfo = null; if (f == null) throw new ArgumentNullException(string.Format("Specification #{0} cannot be null.", index)); using (DynamicParser parser = DynamicParser.Parse(f)) { object result = parser.Result; if (result == null) throw new ArgumentException(string.Format("Specification #{0} resolves to null.", index)); string type = null; string main = null; string owner = null; string alias = null; string condition = null; bool nolock = false; Type tableType = null; // If the expression resolves to a string... if (result is string) { string node = (string)result; int n = node.ToUpper().IndexOf("JOIN "); if (n < 0) main = node; else { // For strings we only accept 'JOIN' as a suffix type = node.Substring(0, n + 4); main = node.Substring(n + 4); } n = main.ToUpper().IndexOf("ON"); if (n >= 0) { condition = main.Substring(n + 3); main = main.Substring(0, n).Trim(); } Tuple tuple = main.SplitSomethingAndAlias(); // In this case we split on the remaining 'main' string[] parts = tuple.Item1.Split('.'); main = Database.StripName(parts.Last()).Validated("Table"); owner = parts.Length == 2 ? Database.StripName(parts.First()).Validated("Owner", canbeNull: true) : null; alias = tuple.Item2.Validated("Alias", canbeNull: true); } else if (result is DynamicParser.Node) { // Or if it resolves to a dynamic node... DynamicParser.Node node = (DynamicParser.Node)result; while (true) { // Support for the ON() virtual method... if (node is DynamicParser.Node.Method && ((DynamicParser.Node.Method)node).Name.ToUpper() == "ON") { if (condition != null) throw new ArgumentException(string.Format("Condition '{0}' is already set when parsing '{1}'.", alias, result)); object[] args = ((DynamicParser.Node.Method)node).Arguments; if (args == null) throw new ArgumentNullException("arg", "ON() is not a parameterless method."); if (args.Length != 1) throw new ArgumentException("ON() requires one and only one parameter: " + args.Sketch()); condition = Parse(args[0], rawstr: true, pars: justAddTables ? null : Parameters); node = node.Host; continue; } // Support for the AS() virtual method... if (node is DynamicParser.Node.Method && ((DynamicParser.Node.Method)node).Name.ToUpper() == "AS") { if (alias != null) throw new ArgumentException(string.Format("Alias '{0}' is already set when parsing '{1}'.", alias, result)); object[] args = ((DynamicParser.Node.Method)node).Arguments; if (args == null) throw new ArgumentNullException("arg", "AS() is not a parameterless method."); if (args.Length != 1) throw new ArgumentException("AS() requires one and only one parameter: " + args.Sketch()); alias = Parse(args[0], rawstr: true, decorate: false, isMultiPart: false).Validated("Alias"); node = node.Host; continue; } // Support for the NoLock() virtual method... if (node is DynamicParser.Node.Method && ((DynamicParser.Node.Method)node).Name.ToUpper() == "NOLOCK") { object[] args = ((DynamicParser.Node.Method)node).Arguments; if (args != null && args.Length > 0) throw new ArgumentNullException("arg", "NoLock() doesn't support arguments."); nolock = true; node = node.Host; continue; } // Support for table specifications... if (node is DynamicParser.Node.GetMember) { if (owner != null) throw new ArgumentException(string.Format("Owner '{0}.{1}' is already set when parsing '{2}'.", owner, main, result)); if (main != null) owner = ((DynamicParser.Node.GetMember)node).Name; else main = ((DynamicParser.Node.GetMember)node).Name; node = node.Host; continue; } // Support for Join Type specifications... if (node is DynamicParser.Node.Method && (node.Host is DynamicParser.Node.Argument || node.Host is DynamicParser.Node.Invoke)) { if (type != null) throw new ArgumentException(string.Format("Join type '{0}' is already set when parsing '{1}'.", main, result)); type = ((DynamicParser.Node.Method)node).Name; bool avoid = false; object[] args = ((DynamicParser.Node.Method)node).Arguments; if (args != null && args.Length > 0) { avoid = args[0] is bool && !((bool)args[0]); string proposedType = args.FirstOrDefault(a => a is string) as string; if (!string.IsNullOrEmpty(proposedType)) type = proposedType; } type = type.ToUpper(); // Normalizing, and stepping out the trivial case... if (type != "JOIN") { // Special cases // x => x.LeftOuter() / x => x.RightOuter()... type = type.Replace("OUTER", " OUTER ") .Replace(" ", " ") .Trim(' '); // x => x.Left()... int n = type.IndexOf("JOIN"); if (n < 0 && !avoid) type += " JOIN"; // x => x.InnerJoin() / x => x.JoinLeft() ... else { if (!avoid) { if (n == 0) type = type.Replace("JOIN", "JOIN "); else type = type.Replace("JOIN", " JOIN"); } } } node = node.Host; continue; } // Support for generic sources... if (node is DynamicParser.Node.Invoke) { if (owner != null) throw new ArgumentException(string.Format("Owner '{0}.{1}' is already set when parsing '{2}'.", owner, main, result)); if (main != null) owner = string.Format("{0}", Parse(node, rawstr: true, pars: justAddTables ? null : Parameters)); else { DynamicParser.Node.Invoke invoke = (DynamicParser.Node.Invoke)node; if (invoke.Arguments.Length == 1 && invoke.Arguments[0] is Type) { tableType = (Type)invoke.Arguments[0]; DynamicTypeMap mapper = DynamicMapperCache.GetMapper(tableType); if (mapper == null) throw new InvalidOperationException(string.Format("Cant assign unmapable type as a table ({0}).", tableType.FullName)); main = mapper.Table == null || string.IsNullOrEmpty(mapper.Table.Name) ? mapper.Type.Name : mapper.Table.Name; owner = (mapper.Table != null) ? mapper.Table.Owner : owner; } else main = string.Format("{0}", Parse(node, rawstr: true, pars: justAddTables ? null : Parameters)); } node = node.Host; continue; } // Just finished the parsing... if (node is DynamicParser.Node.Argument) break; throw new ArgumentException(string.Format("Specification #{0} is invalid: {1}", index, result)); } } else { // Or it is a not supported expression... throw new ArgumentException(string.Format("Specification #{0} is invalid: {1}", index, result)); } // We annotate the aliases being conservative... main = main.Validated("Main"); if (justAddTables) { if (!string.IsNullOrEmpty(main)) tableInfo = tableType == null ? new TableInfo(Database, main, alias, owner, nolock) : new TableInfo(Database, tableType, alias, owner, nolock); else throw new ArgumentException(string.Format("Specification #{0} is invalid: {1}", index, result)); Tables.Add(tableInfo); } else { // Get cached table info tableInfo = string.IsNullOrEmpty(alias) ? Tables.SingleOrDefault(t => t.Name == main && string.IsNullOrEmpty(t.Alias)) : Tables.SingleOrDefault(t => t.Alias == alias); // We finally add the contents if we can... StringBuilder sb = new StringBuilder(); if (string.IsNullOrEmpty(type)) type = "JOIN"; sb.AppendFormat("{0} ", type); if (!string.IsNullOrEmpty(tableInfo.Owner)) sb.AppendFormat("{0}.", Database.DecorateName(tableInfo.Owner)); sb.Append(tableInfo.Name.ContainsAny(StringExtensions.InvalidMemberChars) ? tableInfo.Name : Database.DecorateName(tableInfo.Name)); if (!string.IsNullOrEmpty(tableInfo.Alias)) sb.AppendFormat(" AS {0}", tableInfo.Alias); if (SupportNoLock && tableInfo.NoLock) sb.AppendFormat(" WITH(NOLOCK)"); if (!string.IsNullOrEmpty(condition)) sb.AppendFormat(" ON {0}", condition); _join = string.IsNullOrEmpty(_join) ? sb.ToString() : string.Format("{0} {1}", _join, sb.ToString()); // No comma in this case } } } return this; } #endregion From/Join #region Where /// /// Adds to the 'Where' clause the contents obtained from parsing the dynamic lambda expression given. The condition /// is parsed to the appropriate syntax, where the specific customs virtual methods supported by the parser are used /// as needed. /// - If several Where() methods are chained their contents are, by default, concatenated with an 'AND' operator. /// - The 'And()' and 'Or()' virtual method can be used to concatenate with an 'OR' or an 'AND' operator, as in: /// 'Where( x => x.Or( condition ) )'. /// /// The specification. /// This instance to permit chaining. public virtual IDynamicSelectQueryBuilder Where(Func func) { return this.InternalWhere(func); } /// Add where condition. /// Condition column with operator and value. /// Builder instance. public virtual IDynamicSelectQueryBuilder Where(DynamicColumn column) { return this.InternalWhere(column); } /// Add where condition. /// Condition column. /// Condition operator. /// Condition value. /// Builder instance. public virtual IDynamicSelectQueryBuilder Where(string column, DynamicColumn.CompareOperator op, object value) { return this.InternalWhere(column, op, value); } /// Add where condition. /// Condition column. /// Condition value. /// Builder instance. public virtual IDynamicSelectQueryBuilder Where(string column, object value) { return this.InternalWhere(column, value); } /// Add where condition. /// Set conditions as properties and values of an object. /// If true use schema to determine key columns and ignore those which /// aren't keys. /// Builder instance. public virtual IDynamicSelectQueryBuilder Where(object conditions, bool schema = false) { return this.InternalWhere(conditions, schema); } #endregion Where #region Select /// /// Adds to the 'Select' clause the contents obtained by parsing the dynamic lambda expressions given. The supported /// formats are: /// - Resolve to a string: 'x => "Table.Column AS Alias', where the alias part is optional. /// - Resolve to an expression: 'x => x.Table.Column.As( x.Alias )', where the alias part is optional. /// - Select all columns from a table: 'x => x.Table.All()'. /// - Generic expression: 'x => x( expression ).As( x.Alias )', where the alias part is mandatory. In this case /// the alias is not annotated. /// /// The specification. /// The specification. /// This instance to permit chaining. public virtual IDynamicSelectQueryBuilder Select(Func fn, params Func[] func) { if (fn == null) throw new ArgumentNullException("Array of specifications cannot be null."); int index = SelectFunc(-1, fn); if (func != null) foreach (Func f in func) index = SelectFunc(index, f); return this; } private int SelectFunc(int index, Func f) { index++; if (f == null) throw new ArgumentNullException(string.Format("Specification #{0} cannot be null.", index)); using (DynamicParser parser = DynamicParser.Parse(f)) { object result = parser.Result; if (result == null) throw new ArgumentException(string.Format("Specification #{0} resolves to null.", index)); string main = null; string alias = null; bool all = false; bool anon = false; // If the expression resolves to a string... if (result is string) { string node = (string)result; Tuple tuple = node.SplitSomethingAndAlias(); main = tuple.Item1.Validated("Table and/or Column"); main = FixObjectName(main); alias = tuple.Item2.Validated("Alias", canbeNull: true); } else if (result is DynamicParser.Node) { // Or if it resolves to a dynamic node... ParseSelectNode(result, ref main, ref alias, ref all); } else if (result.GetType().IsAnonymous()) { anon = true; foreach (KeyValuePair prop in result.ToDictionary()) { if (prop.Value is string) { string node = (string)prop.Value; Tuple tuple = node.SplitSomethingAndAlias(); main = FixObjectName(tuple.Item1.Validated("Table and/or Column")); ////alias = tuple.Item2.Validated("Alias", canbeNull: true); } else if (prop.Value is DynamicParser.Node) { // Or if it resolves to a dynamic node... ParseSelectNode(prop.Value, ref main, ref alias, ref all); } else { // Or it is a not supported expression... throw new ArgumentException(string.Format("Specification #{0} in anonymous type is invalid: {1}", index, prop.Value)); } alias = Database.DecorateName(prop.Key); ParseSelectAddColumn(main, alias, all); } } else { // Or it is a not supported expression... throw new ArgumentException(string.Format("Specification #{0} is invalid: {1}", index, result)); } if (!anon) ParseSelectAddColumn(main, alias, all); } return index; } /// Add select columns. /// Columns to add to object. /// Builder instance. public virtual IDynamicSelectQueryBuilder SelectColumn(params DynamicColumn[] columns) { foreach (DynamicColumn col in columns) Select(x => col.ToSQLSelectColumn(Database)); return this; } /// Add select columns. /// Columns to add to object. /// Column format consist of Column Name, Alias and /// Aggregate function in this order separated by ':'. /// Builder instance. public virtual IDynamicSelectQueryBuilder SelectColumn(params string[] columns) { DynamicColumn[] cols = new DynamicColumn[columns.Length]; for (int i = 0; i < columns.Length; i++) cols[i] = DynamicColumn.ParseSelectColumn(columns[i]); return SelectColumn(cols); } #endregion Select #region GroupBy /// /// Adds to the 'Group By' clause the contents obtained from from parsing the dynamic lambda expression given. /// /// The specification. /// The specification. /// This instance to permit chaining. public virtual IDynamicSelectQueryBuilder GroupBy(Func fn, params Func[] func) { if (fn == null) throw new ArgumentNullException("Array of specifications cannot be null."); int index = GroupByFunc(-1, fn); if (func != null) for (int i = 0; i < func.Length; i++) { Func f = func[i]; index = GroupByFunc(index, f); } return this; } private int GroupByFunc(int index, Func f) { index++; if (f == null) throw new ArgumentNullException(string.Format("Specification #{0} cannot be null.", index)); using (DynamicParser parser = DynamicParser.Parse(f)) { object result = parser.Result; if (result == null) throw new ArgumentException(string.Format("Specification #{0} resolves to null.", index)); string main = null; if (result is string) main = FixObjectName(result as string); else main = Parse(result, pars: Parameters); main = main.Validated("Group By"); if (_groupby == null) _groupby = main; else _groupby = string.Format("{0}, {1}", _groupby, main); } return index; } /// Add select columns. /// Columns to group by. /// Builder instance. public virtual IDynamicSelectQueryBuilder GroupByColumn(params DynamicColumn[] columns) { for (int i = 0; i < columns.Length; i++) { DynamicColumn col = columns[i]; GroupBy(x => col.ToSQLGroupByColumn(Database)); } return this; } /// Add select columns. /// Columns to group by. /// Column format consist of Column Name and /// Alias in this order separated by ':'. /// Builder instance. public virtual IDynamicSelectQueryBuilder GroupByColumn(params string[] columns) { return GroupByColumn(columns.Select(c => DynamicColumn.ParseSelectColumn(c)).ToArray()); } #endregion GroupBy #region Having /// /// Adds to the 'Having' clause the contents obtained from parsing the dynamic lambda expression given. The condition /// is parsed to the appropriate syntax, Having the specific customs virtual methods supported by the parser are used /// as needed. /// - If several Having() methods are chained their contents are, by default, concatenated with an 'AND' operator. /// - The 'And()' and 'Or()' virtual method can be used to concatenate with an 'OR' or an 'AND' operator, as in: /// 'Having( x => x.Or( condition ) )'. /// /// The specification. /// This instance to permit chaining. public virtual IDynamicSelectQueryBuilder Having(Func func) { return this.InternalHaving(func); } /// Add Having condition. /// Condition column with operator and value. /// Builder instance. public virtual IDynamicSelectQueryBuilder Having(DynamicColumn column) { return this.InternalHaving(column); } /// Add Having condition. /// Condition column. /// Condition operator. /// Condition value. /// Builder instance. public virtual IDynamicSelectQueryBuilder Having(string column, DynamicColumn.CompareOperator op, object value) { return this.InternalHaving(column, op, value); } /// Add Having condition. /// Condition column. /// Condition value. /// Builder instance. public virtual IDynamicSelectQueryBuilder Having(string column, object value) { return this.InternalHaving(column, value); } /// Add Having condition. /// Set conditions as properties and values of an object. /// If true use schema to determine key columns and ignore those which /// aren't keys. /// Builder instance. public virtual IDynamicSelectQueryBuilder Having(object conditions, bool schema = false) { return this.InternalHaving(conditions, schema); } #endregion Having #region OrderBy /// /// Adds to the 'Order By' clause the contents obtained from from parsing the dynamic lambda expression given. It /// accepts a multipart column specification followed by an optional Ascending() or Descending() virtual methods /// to specify the direction. If no virtual method is used, the default is ascending order. You can also use the /// shorter versions Asc() and Desc(). /// /// The specification. /// The specification. /// This instance to permit chaining. public virtual IDynamicSelectQueryBuilder OrderBy(Func fn, params Func[] func) { if (fn == null) throw new ArgumentNullException("Array of specifications cannot be null."); int index = OrderByFunc(-1, fn); if (func != null) for (int i = 0; i < func.Length; i++) { Func f = func[i]; index = OrderByFunc(index, f); } return this; } private int OrderByFunc(int index, Func f) { index++; if (f == null) throw new ArgumentNullException(string.Format("Specification #{0} cannot be null.", index)); using (DynamicParser parser = DynamicParser.Parse(f)) { object result = parser.Result; if (result == null) throw new ArgumentException(string.Format("Specification #{0} resolves to null.", index)); string main = null; bool ascending = true; if (result is int) main = result.ToString(); else if (result is string) { string[] parts = ((string)result).Split(' '); main = Database.StripName(parts.First()); int colNo; if (!Int32.TryParse(main, out colNo)) main = FixObjectName(main); ascending = parts.Length != 2 || parts.Last().ToUpper() == "ASCENDING" || parts.Last().ToUpper() == "ASC"; } else { // Intercepting trailing 'Ascending' or 'Descending' virtual methods... if (result is DynamicParser.Node.Method) { DynamicParser.Node.Method node = (DynamicParser.Node.Method)result; string name = node.Name.ToUpper(); if (name == "ASCENDING" || name == "ASC" || name == "DESCENDING" || name == "DESC") { object[] args = node.Arguments; if (args != null && !(node.Host is DynamicParser.Node.Argument)) throw new ArgumentException(string.Format("{0} must be a parameterless method, but found: {1}.", name, args.Sketch())); else if ((args == null || args.Length != 1) && node.Host is DynamicParser.Node.Argument) throw new ArgumentException(string.Format("{0} requires one numeric parameter, but found: {1}.", name, args.Sketch())); ascending = (name == "ASCENDING" || name == "ASC") ? true : false; if (args != null && args.Length == 1) { int col = -1; if (args[0] is int) main = args[0].ToString(); else if (args[0] is string) { if (Int32.TryParse(args[0].ToString(), out col)) main = col.ToString(); else main = FixObjectName(args[0].ToString()); } else main = Parse(args[0], pars: Parameters); } result = node.Host; } } // Just parsing the contents... if (!(result is DynamicParser.Node.Argument)) main = Parse(result, pars: Parameters); } main = main.Validated("Order By"); main = string.Format("{0} {1}", main, ascending ? "ASC" : "DESC"); if (_orderby == null) _orderby = main; else _orderby = string.Format("{0}, {1}", _orderby, main); } return index; } /// Add select columns. /// Columns to order by. /// Builder instance. public virtual IDynamicSelectQueryBuilder OrderByColumn(params DynamicColumn[] columns) { for (int i = 0; i < columns.Length; i++) { DynamicColumn col = columns[i]; OrderBy(x => col.ToSQLOrderByColumn(Database)); } return this; } /// Add select columns. /// Columns to order by. /// Column format consist of Column Name and /// Alias in this order separated by ':'. /// Builder instance. public virtual IDynamicSelectQueryBuilder OrderByColumn(params string[] columns) { return OrderByColumn(columns.Select(c => DynamicColumn.ParseOrderByColumn(c)).ToArray()); } #endregion OrderBy #region Top/Limit/Offset/Distinct /// Set top if database support it. /// How many objects select. /// Builder instance. public virtual IDynamicSelectQueryBuilder Top(int? top) { return Limit(top); } /// Set top if database support it. /// How many objects select. /// Builder instance. public virtual IDynamicSelectQueryBuilder Limit(int? limit) { if ((Database.Options & DynamicDatabaseOptions.SupportLimitOffset) != DynamicDatabaseOptions.SupportLimitOffset && (Database.Options & DynamicDatabaseOptions.SupportFirstSkip) != DynamicDatabaseOptions.SupportFirstSkip && (Database.Options & DynamicDatabaseOptions.SupportTop) != DynamicDatabaseOptions.SupportTop) throw new NotSupportedException("Database doesn't support LIMIT clause."); _limit = limit; return this; } /// Set top if database support it. /// How many objects skip selecting. /// Builder instance. public virtual IDynamicSelectQueryBuilder Offset(int? offset) { if ((Database.Options & DynamicDatabaseOptions.SupportLimitOffset) != DynamicDatabaseOptions.SupportLimitOffset && (Database.Options & DynamicDatabaseOptions.SupportFirstSkip) != DynamicDatabaseOptions.SupportFirstSkip) throw new NotSupportedException("Database doesn't support OFFSET clause."); _offset = offset; return this; } /// Set distinct mode. /// Distinct mode. /// Builder instance. public virtual IDynamicSelectQueryBuilder Distinct(bool distinct = true) { _distinct = distinct; return this; } #endregion Top/Limit/Offset/Distinct #region Helpers private void ParseSelectAddColumn(string main, string alias, bool all) { // We annotate the aliases being conservative... main = main.Validated("Main"); ////if (alias != null && !main.ContainsAny(StringExtensions.InvalidMemberChars)) TableAliasList.Add(new KTableAlias(main, alias)); // If all columns are requested... if (all) main += ".*"; // We finally add the contents... string str = (alias == null || all) ? main : string.Format("{0} AS {1}", main, alias); _select = _select == null ? str : string.Format("{0}, {1}", _select, str); } private void ParseSelectNode(object result, ref string column, ref string alias, ref bool all) { string main = null; DynamicParser.Node node = (DynamicParser.Node)result; while (true) { // Support for the AS() virtual method... if (node is DynamicParser.Node.Method && ((DynamicParser.Node.Method)node).Name.ToUpper() == "AS") { if (alias != null) throw new ArgumentException(string.Format("Alias '{0}' is already set when parsing '{1}'.", alias, result)); object[] args = ((DynamicParser.Node.Method)node).Arguments; if (args == null) throw new ArgumentNullException("arg", "AS() is not a parameterless method."); if (args.Length != 1) throw new ArgumentException("AS() requires one and only one parameter: " + args.Sketch()); // Yes, we decorate columns alias = Parse(args[0], rawstr: true, decorate: true, isMultiPart: false).Validated("Alias"); node = node.Host; continue; } // Support for the ALL() virtual method... if (node is DynamicParser.Node.Method && ((DynamicParser.Node.Method)node).Name.ToUpper() == "ALL") { if (all) throw new ArgumentException(string.Format("Flag to select all columns is already set when parsing '{0}'.", result)); object[] args = ((DynamicParser.Node.Method)node).Arguments; if (args != null) throw new ArgumentException("ALL() must be a parameterless virtual method, but found: " + args.Sketch()); all = true; node = node.Host; continue; } // Support for table and/or column specifications... if (node is DynamicParser.Node.GetMember) { if (main != null) throw new ArgumentException(string.Format("Main '{0}' is already set when parsing '{1}'.", main, result)); main = ((DynamicParser.Node.GetMember)node).Name; if (node.Host is DynamicParser.Node.GetMember) { // If leaf then decorate main = Database.DecorateName(main); // Supporting multipart specifications... node = node.Host; // Get table/alias name string table = ((DynamicParser.Node.GetMember)node).Name; bool isAlias = node.Host is DynamicParser.Node.Argument && IsTableAlias(table); if (isAlias) main = string.Format("{0}.{1}", table, main); else if (node.Host is DynamicParser.Node.GetMember) { node = node.Host; main = string.Format("{0}.{1}.{2}", Database.DecorateName(((DynamicParser.Node.GetMember)node).Name), Database.DecorateName(table), main); } else main = string.Format("{0}.{1}", Database.DecorateName(table), main); } else if (node.Host is DynamicParser.Node.Argument) { string table = ((DynamicParser.Node.Argument)node.Host).Name; if (IsTableAlias(table)) main = string.Format("{0}.{1}", table, Database.DecorateName(main)); else if (!IsTableAlias(main)) main = Database.DecorateName(main); } else if (!(node.Host is DynamicParser.Node.Argument && IsTableAlias(main))) main = Database.DecorateName(main); node = node.Host; continue; } // Support for generic sources... if (node is DynamicParser.Node.Invoke) { if (main != null) throw new ArgumentException(string.Format("Main '{0}' is already set when parsing '{1}'.", main, result)); main = string.Format("{0}", Parse(node, rawstr: true, pars: Parameters)); node = node.Host; continue; } // Just finished the parsing... if (node is DynamicParser.Node.Argument) { if (string.IsNullOrEmpty(main) && IsTableAlias(node.Name)) main = node.Name; break; } // All others are assumed to be part of the main element... if (main != null) throw new ArgumentException(string.Format("Main '{0}' is already set when parsing '{1}'.", main, result)); main = Parse(node, pars: Parameters); break; } column = main; } #endregion Helpers #region IExtendedDisposable /// Performs application-defined tasks associated with /// freeing, releasing, or resetting unmanaged resources. public override void Dispose() { base.Dispose(); _select = _from = _join = _groupby = _orderby = null; } #endregion IExtendedDisposable } /// Typed wrapper over with property-to-column translation. /// Mapped entity type. internal class DynamicTypedDeleteQueryBuilder : DynamicDeleteQueryBuilder, IDynamicTypedDeleteQueryBuilder { internal DynamicTypedDeleteQueryBuilder(DynamicDatabase db) : this(db, false) { } internal DynamicTypedDeleteQueryBuilder(DynamicDatabase db, bool mapType) : base(db) { if (mapType) this.Table(typeof(T)); else this.Table(typeof(T).Name); } public IDynamicTypedDeleteQueryBuilder Where(Expression> predicate) { TypedModifyHelper.ApplyWhere((c, o, v) => base.Where(c, o, v), predicate); return this; } public IDynamicTypedDeleteQueryBuilder WhereSql(Func, TypedSqlPredicate> predicate) { string condition = TypedModifyHelper.RenderPredicate(predicate, null, RenderValue, Database.DecorateName, RenderSubQuery); if (string.IsNullOrEmpty(WhereCondition)) WhereCondition = condition; else WhereCondition = string.Format("{0} AND {1}", WhereCondition, condition); return this; } public new IDynamicTypedDeleteQueryBuilder Where(Func func) { base.Where(func); return this; } public new IDynamicTypedDeleteQueryBuilder Where(DynamicColumn column) { base.Where(column); return this; } public new IDynamicTypedDeleteQueryBuilder Where(string column, DynamicColumn.CompareOperator op, object value) { base.Where(column, op, value); return this; } public new IDynamicTypedDeleteQueryBuilder Where(string column, object value) { base.Where(column, value); return this; } public new IDynamicTypedDeleteQueryBuilder Where(object conditions, bool schema = false) { base.Where(conditions, schema); return this; } private string RenderValue(object value) { if (value == null) return "NULL"; DynamicSchemaColumn? columnSchema = null; return ParseConstant(value, Parameters, columnSchema); } private string RenderSubQuery(Builders.IDynamicSelectQueryBuilder query) { foreach (KeyValuePair item in query.Parameters) if (!Parameters.ContainsKey(item.Key)) Parameters.Add(item.Key, item.Value); return query.CommandText(); } } /// Typed wrapper over with property-to-column translation. /// Mapped entity type. internal class DynamicTypedInsertQueryBuilder : DynamicInsertQueryBuilder, IDynamicTypedInsertQueryBuilder { internal DynamicTypedInsertQueryBuilder(DynamicDatabase db) : this(db, false) { } internal DynamicTypedInsertQueryBuilder(DynamicDatabase db, bool mapType) : base(db) { if (mapType) this.Table(typeof(T)); else this.Table(typeof(T).Name); } public IDynamicTypedInsertQueryBuilder Insert(Expression> selector, object value) { base.Insert(TypedModifyHelper.GetMappedColumn(selector), value); return this; } public IDynamicTypedInsertQueryBuilder Insert(T value) { base.Insert(value); return this; } public IDynamicTypedInsertQueryBuilder InsertSql(Expression> selector, Func, TypedSqlExpression> valueFactory) { string column = FixObjectName(TypedModifyHelper.GetMappedColumn(selector), onlyColumn: true); string value = TypedModifyHelper.RenderExpression(valueFactory, null, RenderValue, Database.DecorateName, RenderSubQuery); _columns = _columns == null ? column : string.Format("{0}, {1}", _columns, column); _values = _values == null ? value : string.Format("{0}, {1}", _values, value); return this; } public IDynamicTypedInsertQueryBuilder InsertSql(Func, object> values) { if (values == null) throw new ArgumentNullException("values"); object data = values(new TypedTableContext(null)); foreach (KeyValuePair item in data.ToDictionary()) { string column = FixObjectName(TypedModifyHelper.GetMappedColumnByName(typeof(T), item.Key), onlyColumn: true); string value = (item.Value as TypedSqlExpression) != null ? TypedModifyHelper.RenderExpression(u => (TypedSqlExpression)item.Value, null, RenderValue, Database.DecorateName, RenderSubQuery) : RenderValue(item.Value); _columns = _columns == null ? column : string.Format("{0}, {1}", _columns, column); _values = _values == null ? value : string.Format("{0}, {1}", _values, value); } return this; } public new IDynamicTypedInsertQueryBuilder Values(Func fn, params Func[] func) { base.Values(fn, func); return this; } public new IDynamicTypedInsertQueryBuilder Insert(string column, object value) { base.Insert(column, value); return this; } public new IDynamicTypedInsertQueryBuilder Insert(object o) { base.Insert(o); return this; } private string RenderValue(object value) { if (value == null) return "NULL"; DynamicSchemaColumn? columnSchema = null; return ParseConstant(value, Parameters, columnSchema); } private string RenderSubQuery(Builders.IDynamicSelectQueryBuilder query) { foreach (KeyValuePair item in query.Parameters) if (!Parameters.ContainsKey(item.Key)) Parameters.Add(item.Key, item.Value); return query.CommandText(); } } /// Typed wrapper over with property-to-column translation. /// Mapped entity type. internal class DynamicTypedSelectQueryBuilder : DynamicSelectQueryBuilder, IDynamicTypedSelectQueryBuilder { private sealed class TypedJoinInfo { public Type ModelType { get; set; } public string Alias { get; set; } } private sealed class TypedSqlRenderContext : ITypedSqlRenderContext { private readonly DynamicTypedSelectQueryBuilder _builder; public TypedSqlRenderContext(DynamicTypedSelectQueryBuilder builder) { _builder = builder; } public string ResolveColumn(Type modelType, string memberName, string alias) { DynamicTypeMap mapper = DynamicMapperCache.GetMapper(modelType); if (mapper == null) throw new InvalidOperationException(string.Format("Cant assign unmapable type as a table ({0}).", modelType.FullName)); string mappedColumn = mapper.PropertyMap.TryGetValue(memberName) ?? mapper.PropertyMap.Where(x => string.Equals(x.Key, memberName, StringComparison.OrdinalIgnoreCase)).Select(x => x.Value).FirstOrDefault() ?? memberName; return string.IsNullOrEmpty(alias) ? _builder.Database.DecorateName(mappedColumn) : string.Format("{0}.{1}", alias, _builder.Database.DecorateName(mappedColumn)); } public string RenderValue(object value) { if (value == null) return "NULL"; DynamicSchemaColumn? columnSchema = null; return _builder.ParseConstant(value, _builder.Parameters, columnSchema); } public string DecorateName(string name) { return _builder.Database.DecorateName(name); } public string RenderSubQuery(IDynamicSelectQueryBuilder query) { if (query == null) throw new ArgumentNullException("query"); foreach (var item in query.Parameters) if (!_builder.Parameters.ContainsKey(item.Key)) _builder.Parameters.Add(item.Key, item.Value); return query.CommandText(); } } private readonly DynamicTypeMap _mapper; private readonly List _typedJoins = new List(); public DynamicTypedSelectQueryBuilder(DynamicDatabase db) : base(db) { _mapper = DynamicMapperCache.GetMapper() ?? throw new InvalidOperationException(string.Format("Cant assign unmapable type as a table ({0}).", typeof(T).FullName)); } public IDynamicTypedSelectQueryBuilder Where(Expression> predicate) { if (predicate == null) throw new ArgumentNullException("predicate"); string condition = ParseTypedCondition(predicate.Body); if (string.IsNullOrEmpty(WhereCondition)) WhereCondition = condition; else WhereCondition = string.Format("{0} AND {1}", WhereCondition, condition); return this; } public IDynamicTypedSelectQueryBuilder Join(Expression> on, string alias = null, DynamicJoinType joinType = DynamicJoinType.Inner, bool noLock = false) { return Join(on, alias, GetJoinKeyword(joinType), noLock); } public IDynamicTypedSelectQueryBuilder Join(Expression> on, string alias, string joinType, bool noLock = false) { if (string.IsNullOrEmpty(joinType)) throw new ArgumentNullException("joinType"); DynamicTypeMap rightMapper = DynamicMapperCache.GetMapper(typeof(TRight)); if (rightMapper == null) throw new InvalidOperationException(string.Format("Cant assign unmapable type as a table ({0}).", typeof(TRight).FullName)); string rightTable = rightMapper.Table == null || string.IsNullOrEmpty(rightMapper.Table.Name) ? typeof(TRight).Name : rightMapper.Table.Name; string rightOwner = rightMapper.Table == null ? null : rightMapper.Table.Owner; string rightAlias = string.IsNullOrEmpty(alias) ? "t" + (Tables.Count + 1).ToString() : alias; string leftPrefix = GetRootAliasOrTableName(); if (string.IsNullOrEmpty(leftPrefix)) throw new InvalidOperationException("Join requires source table to be present."); string condition = null; if (on != null) condition = ParseTypedJoinCondition(on.Body, leftPrefix, rightAlias, _mapper, rightMapper, on.Parameters[0], on.Parameters[1]); string ownerPrefix = string.IsNullOrEmpty(rightOwner) ? string.Empty : Database.DecorateName(rightOwner) + "."; string rightTableExpr = ownerPrefix + Database.DecorateName(rightTable); string joinExpr = string.Format("{0} {1} AS {2}", joinType.Trim(), rightTableExpr, rightAlias); if (SupportNoLock && noLock) joinExpr += " WITH(NOLOCK)"; if (!string.IsNullOrEmpty(condition)) joinExpr += string.Format(" ON {0}", condition); RegisterTypedJoin(typeof(TRight), rightAlias); AppendJoinClause(joinExpr); return this; } public IDynamicTypedSelectQueryBuilder Join(Func, TypedJoinBuilder> specification) { if (specification == null) throw new ArgumentNullException("specification"); TypedJoinBuilder spec = specification(new TypedJoinBuilder()); if (spec == null) throw new ArgumentException("Join specification cannot resolve to null.", "specification"); if (spec.OnPredicate != null) return Join(spec.OnPredicate, spec.Alias, spec.CustomJoinType ?? GetJoinKeyword(spec.JoinType), spec.UseNoLock); DynamicTypeMap rightMapper = DynamicMapperCache.GetMapper(typeof(TRight)); if (rightMapper == null) throw new InvalidOperationException(string.Format("Cant assign unmapable type as a table ({0}).", typeof(TRight).FullName)); string rightTable = rightMapper.Table == null || string.IsNullOrEmpty(rightMapper.Table.Name) ? typeof(TRight).Name : rightMapper.Table.Name; string rightOwner = rightMapper.Table == null ? null : rightMapper.Table.Owner; string rightAlias = string.IsNullOrEmpty(spec.Alias) ? "t" + (Tables.Count + 1).ToString() : spec.Alias; string ownerPrefix = string.IsNullOrEmpty(rightOwner) ? string.Empty : Database.DecorateName(rightOwner) + "."; string rightTableExpr = ownerPrefix + Database.DecorateName(rightTable); string joinExpr = string.Format("{0} {1} AS {2}", (spec.CustomJoinType ?? GetJoinKeyword(spec.JoinType)).Trim(), rightTableExpr, rightAlias); if (SupportNoLock && spec.UseNoLock) joinExpr += " WITH(NOLOCK)"; if (spec.OnSqlPredicate != null) { TypedSqlRenderContext context = new TypedSqlRenderContext(this); joinExpr += string.Format(" ON {0}", spec.OnSqlPredicate(new TypedTableContext(GetRootAliasOrTableName()), new TypedTableContext(rightAlias)).Render(context)); } else if (!string.IsNullOrEmpty(spec.OnRawCondition)) joinExpr += string.Format(" ON {0}", spec.OnRawCondition); RegisterTypedJoin(typeof(TRight), rightAlias); AppendJoinClause(joinExpr); return this; } public new IDynamicTypedSelectQueryBuilder Join(params Func[] func) { base.Join(func); return this; } public new IDynamicTypedSelectQueryBuilder Where(DynamicColumn column) { base.Where(column); return this; } public new IDynamicTypedSelectQueryBuilder Where(string column, DynamicColumn.CompareOperator op, object value) { base.Where(column, op, value); return this; } public new IDynamicTypedSelectQueryBuilder Where(string column, object value) { base.Where(column, value); return this; } public new IDynamicTypedSelectQueryBuilder Where(object conditions, bool schema = false) { base.Where(conditions, schema); return this; } public IDynamicTypedSelectQueryBuilder Select(Expression> selector, params Expression>[] selectors) { if (selector == null) throw new ArgumentNullException("selector"); AddSelectSelector(selector); if (selectors != null) foreach (var s in selectors) { if (s == null) throw new ArgumentNullException("selectors", "Array of selectors cannot contain null."); AddSelectSelector(s); } return this; } public IDynamicTypedSelectQueryBuilder SelectSql(Func, TypedSqlSelectable> selector, params Func, TypedSqlSelectable>[] selectors) { if (selector == null) throw new ArgumentNullException("selector"); AddSelectSqlSelector(selector); if (selectors != null) foreach (Func, TypedSqlSelectable> item in selectors) AddSelectSqlSelector(item); return this; } public IDynamicTypedSelectQueryBuilder SelectSql(Func, TypedTableContext, TypedSqlSelectable> selector, params Func, TypedTableContext, TypedSqlSelectable>[] selectors) { if (selector == null) throw new ArgumentNullException("selector"); AddSelectSqlSelector(selector(GetRootContext(), GetJoinedContext(0))); if (selectors != null) foreach (Func, TypedTableContext, TypedSqlSelectable> item in selectors) AddSelectSqlSelector(item(GetRootContext(), GetJoinedContext(0))); return this; } public IDynamicTypedSelectQueryBuilder SelectSql(Func, TypedTableContext, TypedTableContext, TypedSqlSelectable> selector, params Func, TypedTableContext, TypedTableContext, TypedSqlSelectable>[] selectors) { if (selector == null) throw new ArgumentNullException("selector"); AddSelectSqlSelector(selector(GetRootContext(), GetJoinedContext(0), GetJoinedContext(1))); if (selectors != null) foreach (Func, TypedTableContext, TypedTableContext, TypedSqlSelectable> item in selectors) AddSelectSqlSelector(item(GetRootContext(), GetJoinedContext(0), GetJoinedContext(1))); return this; } public IDynamicTypedSelectQueryBuilder SelectSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable>[] selectors) { if (selector == null) throw new ArgumentNullException("selector"); AddSelectSqlSelector(selector(GetRootContext(), GetJoinedContext(0), GetJoinedContext(1), GetJoinedContext(2))); if (selectors != null) foreach (Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable> item in selectors) AddSelectSqlSelector(item(GetRootContext(), GetJoinedContext(0), GetJoinedContext(1), GetJoinedContext(2))); return this; } public IDynamicTypedSelectQueryBuilder SelectSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable>[] selectors) { if (selector == null) throw new ArgumentNullException("selector"); AddSelectSqlSelector(selector(GetRootContext(), GetJoinedContext(0), GetJoinedContext(1), GetJoinedContext(2), GetJoinedContext(3))); if (selectors != null) foreach (Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable> item in selectors) AddSelectSqlSelector(item(GetRootContext(), GetJoinedContext(0), GetJoinedContext(1), GetJoinedContext(2), GetJoinedContext(3))); return this; } public IDynamicTypedSelectQueryBuilder GroupBy(Expression> selector, params Expression>[] selectors) { if (selector == null) throw new ArgumentNullException("selector"); AddGroupBySelector(selector); if (selectors != null) foreach (var s in selectors) { if (s == null) throw new ArgumentNullException("selectors", "Array of selectors cannot contain null."); AddGroupBySelector(s); } return this; } public IDynamicTypedSelectQueryBuilder GroupBySql(Func, TypedSqlExpression> selector, params Func, TypedSqlExpression>[] selectors) { if (selector == null) throw new ArgumentNullException("selector"); AddGroupBySqlSelector(selector); if (selectors != null) foreach (Func, TypedSqlExpression> item in selectors) AddGroupBySqlSelector(item); return this; } public IDynamicTypedSelectQueryBuilder GroupBySql(Func, TypedTableContext, TypedSqlExpression> selector, params Func, TypedTableContext, TypedSqlExpression>[] selectors) { if (selector == null) throw new ArgumentNullException("selector"); AddGroupBySqlSelector(selector(GetRootContext(), GetJoinedContext(0))); if (selectors != null) foreach (Func, TypedTableContext, TypedSqlExpression> item in selectors) AddGroupBySqlSelector(item(GetRootContext(), GetJoinedContext(0))); return this; } public IDynamicTypedSelectQueryBuilder GroupBySql(Func, TypedTableContext, TypedTableContext, TypedSqlExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedSqlExpression>[] selectors) { if (selector == null) throw new ArgumentNullException("selector"); AddGroupBySqlSelector(selector(GetRootContext(), GetJoinedContext(0), GetJoinedContext(1))); if (selectors != null) foreach (Func, TypedTableContext, TypedTableContext, TypedSqlExpression> item in selectors) AddGroupBySqlSelector(item(GetRootContext(), GetJoinedContext(0), GetJoinedContext(1))); return this; } public IDynamicTypedSelectQueryBuilder GroupBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression>[] selectors) { if (selector == null) throw new ArgumentNullException("selector"); AddGroupBySqlSelector(selector(GetRootContext(), GetJoinedContext(0), GetJoinedContext(1), GetJoinedContext(2))); if (selectors != null) foreach (Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression> item in selectors) AddGroupBySqlSelector(item(GetRootContext(), GetJoinedContext(0), GetJoinedContext(1), GetJoinedContext(2))); return this; } public IDynamicTypedSelectQueryBuilder GroupBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression>[] selectors) { if (selector == null) throw new ArgumentNullException("selector"); AddGroupBySqlSelector(selector(GetRootContext(), GetJoinedContext(0), GetJoinedContext(1), GetJoinedContext(2), GetJoinedContext(3))); if (selectors != null) foreach (Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression> item in selectors) AddGroupBySqlSelector(item(GetRootContext(), GetJoinedContext(0), GetJoinedContext(1), GetJoinedContext(2), GetJoinedContext(3))); return this; } public IDynamicTypedSelectQueryBuilder OrderBy(Expression> selector, params Expression>[] selectors) { if (selector == null) throw new ArgumentNullException("selector"); AddOrderBySelector(selector); if (selectors != null) foreach (var s in selectors) { if (s == null) throw new ArgumentNullException("selectors", "Array of selectors cannot contain null."); AddOrderBySelector(s); } return this; } public IDynamicTypedSelectQueryBuilder OrderBySql(Func, TypedSqlOrderExpression> selector, params Func, TypedSqlOrderExpression>[] selectors) { if (selector == null) throw new ArgumentNullException("selector"); AddOrderBySqlSelector(selector); if (selectors != null) foreach (Func, TypedSqlOrderExpression> item in selectors) AddOrderBySqlSelector(item); return this; } public IDynamicTypedSelectQueryBuilder OrderBySql(Func, TypedTableContext, TypedSqlOrderExpression> selector, params Func, TypedTableContext, TypedSqlOrderExpression>[] selectors) { if (selector == null) throw new ArgumentNullException("selector"); AddOrderBySqlSelector(selector(GetRootContext(), GetJoinedContext(0))); if (selectors != null) foreach (Func, TypedTableContext, TypedSqlOrderExpression> item in selectors) AddOrderBySqlSelector(item(GetRootContext(), GetJoinedContext(0))); return this; } public IDynamicTypedSelectQueryBuilder OrderBySql(Func, TypedTableContext, TypedTableContext, TypedSqlOrderExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedSqlOrderExpression>[] selectors) { if (selector == null) throw new ArgumentNullException("selector"); AddOrderBySqlSelector(selector(GetRootContext(), GetJoinedContext(0), GetJoinedContext(1))); if (selectors != null) foreach (Func, TypedTableContext, TypedTableContext, TypedSqlOrderExpression> item in selectors) AddOrderBySqlSelector(item(GetRootContext(), GetJoinedContext(0), GetJoinedContext(1))); return this; } public IDynamicTypedSelectQueryBuilder OrderBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression>[] selectors) { if (selector == null) throw new ArgumentNullException("selector"); AddOrderBySqlSelector(selector(GetRootContext(), GetJoinedContext(0), GetJoinedContext(1), GetJoinedContext(2))); if (selectors != null) foreach (Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression> item in selectors) AddOrderBySqlSelector(item(GetRootContext(), GetJoinedContext(0), GetJoinedContext(1), GetJoinedContext(2))); return this; } public IDynamicTypedSelectQueryBuilder OrderBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression>[] selectors) { if (selector == null) throw new ArgumentNullException("selector"); AddOrderBySqlSelector(selector(GetRootContext(), GetJoinedContext(0), GetJoinedContext(1), GetJoinedContext(2), GetJoinedContext(3))); if (selectors != null) foreach (Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression> item in selectors) AddOrderBySqlSelector(item(GetRootContext(), GetJoinedContext(0), GetJoinedContext(1), GetJoinedContext(2), GetJoinedContext(3))); return this; } public new IDynamicTypedSelectQueryBuilder Top(int? top) { base.Top(top); return this; } public new IDynamicTypedSelectQueryBuilder Limit(int? limit) { base.Limit(limit); return this; } public new IDynamicTypedSelectQueryBuilder Offset(int? offset) { base.Offset(offset); return this; } public new IDynamicTypedSelectQueryBuilder Distinct(bool distinct = true) { base.Distinct(distinct); return this; } public IDynamicTypedSelectQueryBuilder Having(Expression> predicate) { if (predicate == null) throw new ArgumentNullException("predicate"); string condition = ParseTypedCondition(predicate.Body); if (string.IsNullOrEmpty(HavingCondition)) HavingCondition = condition; else HavingCondition = string.Format("{0} AND {1}", HavingCondition, condition); return this; } public IDynamicTypedSelectQueryBuilder WhereSql(Func, TypedSqlPredicate> predicate) { if (predicate == null) throw new ArgumentNullException("predicate"); AppendSqlCondition(RenderSqlPredicate(predicate), false); return this; } public IDynamicTypedSelectQueryBuilder WhereSql(Func, TypedTableContext, TypedSqlPredicate> predicate) { if (predicate == null) throw new ArgumentNullException("predicate"); AppendSqlCondition(RenderSqlPredicate(predicate(GetRootContext(), GetJoinedContext(0))), false); return this; } public IDynamicTypedSelectQueryBuilder WhereSql(Func, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate) { if (predicate == null) throw new ArgumentNullException("predicate"); AppendSqlCondition(RenderSqlPredicate(predicate(GetRootContext(), GetJoinedContext(0), GetJoinedContext(1))), false); return this; } public IDynamicTypedSelectQueryBuilder WhereSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate) { if (predicate == null) throw new ArgumentNullException("predicate"); AppendSqlCondition(RenderSqlPredicate(predicate(GetRootContext(), GetJoinedContext(0), GetJoinedContext(1), GetJoinedContext(2))), false); return this; } public IDynamicTypedSelectQueryBuilder WhereSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate) { if (predicate == null) throw new ArgumentNullException("predicate"); AppendSqlCondition(RenderSqlPredicate(predicate(GetRootContext(), GetJoinedContext(0), GetJoinedContext(1), GetJoinedContext(2), GetJoinedContext(3))), false); return this; } public IDynamicTypedSelectQueryBuilder HavingSql(Func, TypedSqlPredicate> predicate) { if (predicate == null) throw new ArgumentNullException("predicate"); AppendSqlCondition(RenderSqlPredicate(predicate), true); return this; } public IDynamicTypedSelectQueryBuilder HavingSql(Func, TypedTableContext, TypedSqlPredicate> predicate) { if (predicate == null) throw new ArgumentNullException("predicate"); AppendSqlCondition(RenderSqlPredicate(predicate(GetRootContext(), GetJoinedContext(0))), true); return this; } public IDynamicTypedSelectQueryBuilder HavingSql(Func, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate) { if (predicate == null) throw new ArgumentNullException("predicate"); AppendSqlCondition(RenderSqlPredicate(predicate(GetRootContext(), GetJoinedContext(0), GetJoinedContext(1))), true); return this; } public IDynamicTypedSelectQueryBuilder HavingSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate) { if (predicate == null) throw new ArgumentNullException("predicate"); AppendSqlCondition(RenderSqlPredicate(predicate(GetRootContext(), GetJoinedContext(0), GetJoinedContext(1), GetJoinedContext(2))), true); return this; } public IDynamicTypedSelectQueryBuilder HavingSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate) { if (predicate == null) throw new ArgumentNullException("predicate"); AppendSqlCondition(RenderSqlPredicate(predicate(GetRootContext(), GetJoinedContext(0), GetJoinedContext(1), GetJoinedContext(2), GetJoinedContext(3))), true); return this; } public new IDynamicTypedSelectQueryBuilder Having(DynamicColumn column) { base.Having(column); return this; } public new IDynamicTypedSelectQueryBuilder Having(string column, DynamicColumn.CompareOperator op, object value) { base.Having(column, op, value); return this; } public new IDynamicTypedSelectQueryBuilder Having(string column, object value) { base.Having(column, value); return this; } public new IDynamicTypedSelectQueryBuilder Having(object conditions, bool schema = false) { base.Having(conditions, schema); return this; } private void AddSelectSelector(Expression> selector) { var body = UnwrapConvert(selector.Body); if (body is NewExpression ne) { foreach (var argument in ne.Arguments) { var parsed = ParseTypedSelectExpression(argument); ((IDynamicSelectQueryBuilder)this).Select(x => parsed); } } else { var parsed = ParseTypedSelectExpression(body); ((IDynamicSelectQueryBuilder)this).Select(x => parsed); } } private void AddSelectSqlSelector(Func, TypedSqlSelectable> selector) { if (selector == null) throw new ArgumentNullException("selector"); AddSelectSqlSelector(selector(GetRootContext())); } internal void AddSelectSqlSelector(TypedSqlSelectable item) { if (item == null) throw new ArgumentNullException("item"); TypedSqlRenderContext context = new TypedSqlRenderContext(this); string rendered = item.Render(context); _select = string.IsNullOrEmpty(_select) ? rendered : string.Format("{0}, {1}", _select, rendered); } private void AddGroupBySelector(Expression> selector) { var body = UnwrapConvert(selector.Body); if (body is NewExpression ne) { foreach (var argument in ne.Arguments) { var parsed = ParseTypedMemberAccess(argument); ((IDynamicSelectQueryBuilder)this).GroupBy(x => parsed); } } else { var parsed = ParseTypedMemberAccess(body); ((IDynamicSelectQueryBuilder)this).GroupBy(x => parsed); } } private void AddGroupBySqlSelector(Func, TypedSqlExpression> selector) { if (selector == null) throw new ArgumentNullException("selector"); AddGroupBySqlSelector(selector(GetRootContext())); } internal void AddGroupBySqlSelector(TypedSqlExpression item) { if (item == null) throw new ArgumentNullException("item"); TypedSqlRenderContext context = new TypedSqlRenderContext(this); string rendered = item.Render(context); _groupby = string.IsNullOrEmpty(_groupby) ? rendered : string.Format("{0}, {1}", _groupby, rendered); } private void AddOrderBySelector(Expression> selector) { var body = UnwrapConvert(selector.Body); bool ascending = true; if (body is MethodCallExpression call && IsAscOrDesc(call)) { ascending = call.Method.Name.ToUpper() != "DESC"; body = UnwrapConvert(call.Object ?? call.Arguments.FirstOrDefault()); } string main = ParseTypedMemberAccess(body); string parsed = string.Format("{0} {1}", main, ascending ? "ASC" : "DESC"); ((IDynamicSelectQueryBuilder)this).OrderBy(x => parsed); } private void AddOrderBySqlSelector(Func, TypedSqlOrderExpression> selector) { if (selector == null) throw new ArgumentNullException("selector"); AddOrderBySqlSelector(selector(GetRootContext())); } internal void AddOrderBySqlSelector(TypedSqlOrderExpression item) { TypedSqlRenderContext context = new TypedSqlRenderContext(this); string rendered = item.Render(context); _orderby = string.IsNullOrEmpty(_orderby) ? rendered : string.Format("{0}, {1}", _orderby, rendered); } private string RenderSqlPredicate(Func, TypedSqlPredicate> predicate) { return RenderSqlPredicate(predicate(GetRootContext())); } private string RenderSqlPredicate(TypedSqlPredicate predicate) { if (predicate == null) throw new ArgumentNullException("predicate"); TypedSqlRenderContext context = new TypedSqlRenderContext(this); return predicate.Render(context); } internal string RenderScopeSqlPredicate(TypedSqlPredicate predicate) { return RenderSqlPredicate(predicate); } private string ParseTypedCondition(Expression expression) { expression = UnwrapConvert(expression); if (expression is BinaryExpression binary) { switch (binary.NodeType) { case ExpressionType.AndAlso: case ExpressionType.And: return string.Format("({0} AND {1})", ParseTypedCondition(binary.Left), ParseTypedCondition(binary.Right)); case ExpressionType.OrElse: case ExpressionType.Or: return string.Format("({0} OR {1})", ParseTypedCondition(binary.Left), ParseTypedCondition(binary.Right)); case ExpressionType.Equal: case ExpressionType.NotEqual: case ExpressionType.GreaterThan: case ExpressionType.GreaterThanOrEqual: case ExpressionType.LessThan: case ExpressionType.LessThanOrEqual: { DynamicSchemaColumn? columnSchema = null; string left = ParseTypedValue(binary.Left, ref columnSchema); string right = ParseTypedValue(binary.Right, ref columnSchema); string op = GetBinaryOperator(binary.NodeType, IsNullConstant(binary.Right)); return string.Format("({0} {1} {2})", left, op, right); } } } if (expression is UnaryExpression unary && unary.NodeType == ExpressionType.Not) return string.Format("(NOT {0})", ParseTypedCondition(unary.Operand)); if (expression is MethodCallExpression call && IsEnumerableContains(call)) { DynamicSchemaColumn? col = null; return ParseTypedContains(call, ref col); } throw new NotSupportedException(string.Format("Typed fluent where expression is not supported: {0}", expression)); } private string ParseTypedValue(Expression expression, ref DynamicSchemaColumn? columnSchema) { expression = UnwrapConvert(expression); if (IsMemberFromTypedParameter(expression)) { string col = ParseTypedMemberAccess(expression); columnSchema = GetColumnFromSchema(col); return col; } if (expression is MethodCallExpression call && IsEnumerableContains(call)) return ParseTypedContains(call, ref columnSchema); object value = EvaluateExpression(expression); return ParseConstant(value, Parameters, columnSchema); } private string ParseTypedContains(MethodCallExpression call, ref DynamicSchemaColumn? columnSchema) { // Supports: list.Contains(x.Property) and Enumerable.Contains(list, x.Property) Expression collection; Expression candidate; if (call.Object != null) { collection = call.Object; candidate = call.Arguments[0]; } else { collection = call.Arguments[0]; candidate = call.Arguments[1]; } candidate = UnwrapConvert(candidate); if (!IsMemberFromTypedParameter(candidate)) throw new NotSupportedException(string.Format("Typed Contains() must target a mapped member: {0}", call)); string left = ParseTypedMemberAccess(candidate); columnSchema = GetColumnFromSchema(left); var values = EvaluateExpression(collection) as IEnumerable; if (values == null && collection is MethodCallExpression implicitCall && string.Equals(implicitCall.Method.Name, "op_Implicit", StringComparison.Ordinal) && implicitCall.Arguments.Count > 0) { values = EvaluateExpression(implicitCall.Arguments[0]) as IEnumerable; } if (values == null) throw new NotSupportedException(string.Format("Typed Contains() source is not enumerable: {0}", call)); var inList = new List(); foreach (var item in values.Cast()) inList.Add(ParseConstant(item, Parameters, columnSchema)); if (!inList.Any()) return "(1 = 0)"; return string.Format("({0} IN({1}))", left, string.Join(", ", inList)); } private string ParseTypedMemberAccess(Expression expression) { expression = UnwrapConvert(expression); if (!(expression is MemberExpression member) || !IsMemberFromTypedParameter(member)) throw new NotSupportedException(string.Format("Typed fluent member access is not supported: {0}", expression)); string mappedColumn = null; var property = member.Member as PropertyInfo; if (property != null) { var attrs = property.GetCustomAttributes(typeof(ColumnAttribute), true); var colAttr = attrs == null ? null : attrs.Cast().FirstOrDefault(); if (colAttr != null && !string.IsNullOrEmpty(colAttr.Name)) mappedColumn = colAttr.Name; } if (string.IsNullOrEmpty(mappedColumn)) { mappedColumn = _mapper.PropertyMap.TryGetValue(member.Member.Name) ?? _mapper.PropertyMap .Where(x => string.Equals(x.Key, member.Member.Name, StringComparison.OrdinalIgnoreCase)) .Select(x => x.Value) .FirstOrDefault() ?? _mapper.ColumnsMap .Where(x => string.Equals(x.Value.Name, member.Member.Name, StringComparison.OrdinalIgnoreCase)) .Select(x => x.Key) .FirstOrDefault() ?? member.Member.Name; } string tablePrefix = GetRootAliasOrTableName(); string qualified = string.IsNullOrEmpty(tablePrefix) ? mappedColumn : string.Format("{0}.{1}", tablePrefix, mappedColumn); return FixObjectName(qualified); } private string ParseTypedSelectExpression(Expression expression) { expression = UnwrapConvert(expression); if (expression is MethodCallExpression call && IsAsCall(call)) { string left = ParseTypedMemberAccess(call.Object ?? call.Arguments.FirstOrDefault()); var alias = EvaluateExpression(call.Arguments.Last()) == null ? null : EvaluateExpression(call.Arguments.Last()).ToString(); alias = alias.Validated("Alias"); return string.Format("{0} AS {1}", left, Database.DecorateName(alias)); } return ParseTypedMemberAccess(expression); } private string GetRootAliasOrTableName() { var mappedTable = _mapper.Table == null || string.IsNullOrEmpty(_mapper.Table.Name) ? _mapper.Type.Name : _mapper.Table.Name; var table = Tables.FirstOrDefault(t => t.Name == mappedTable || t.Name == Database.StripName(mappedTable)); if (table == null) table = Tables.FirstOrDefault(); if (table == null) return null; return string.IsNullOrEmpty(table.Alias) ? table.Name : table.Alias; } private TypedTableContext GetRootContext() { return new TypedTableContext(GetRootAliasOrTableName()); } private TypedTableContext GetJoinedContext(int index) { if (index < 0 || index >= _typedJoins.Count) throw new InvalidOperationException(string.Format("Typed join context at position {0} is not available.", index + 1)); TypedJoinInfo join = _typedJoins[index]; if (join.ModelType != typeof(TJoin)) throw new InvalidOperationException(string.Format("Typed join context at position {0} is {1}, not {2}.", index + 1, join.ModelType.FullName, typeof(TJoin).FullName)); return new TypedTableContext(join.Alias); } private void RegisterTypedJoin(Type modelType, string alias) { _typedJoins.Add(new TypedJoinInfo { ModelType = modelType, Alias = alias }); } internal void AppendSqlCondition(string condition, bool having) { if (having) { if (string.IsNullOrEmpty(HavingCondition)) HavingCondition = condition; else HavingCondition = string.Format("{0} AND {1}", HavingCondition, condition); } else { if (string.IsNullOrEmpty(WhereCondition)) WhereCondition = condition; else WhereCondition = string.Format("{0} AND {1}", WhereCondition, condition); } } private static bool IsMemberFromTypedParameter(Expression expression) { var member = expression as MemberExpression; if (member == null) return false; var parameter = member.Expression as ParameterExpression; return parameter != null && parameter.Type == typeof(T); } private static string GetJoinKeyword(DynamicJoinType joinType) { switch (joinType) { case DynamicJoinType.Join: return "JOIN"; case DynamicJoinType.Left: return "LEFT JOIN"; case DynamicJoinType.Right: return "RIGHT JOIN"; case DynamicJoinType.Full: return "FULL JOIN"; case DynamicJoinType.LeftOuter: return "LEFT OUTER JOIN"; case DynamicJoinType.RightOuter: return "RIGHT OUTER JOIN"; case DynamicJoinType.FullOuter: return "FULL OUTER JOIN"; default: return "INNER JOIN"; } } private void AppendJoinClause(string joinClause) { _join = string.IsNullOrEmpty(_join) ? joinClause : string.Format("{0} {1}", _join, joinClause); } private string ParseTypedJoinCondition(Expression expression, string leftPrefix, string rightPrefix, DynamicTypeMap leftMapper, DynamicTypeMap rightMapper, ParameterExpression leftParameter, ParameterExpression rightParameter) { expression = UnwrapConvert(expression); if (expression is BinaryExpression binary) { switch (binary.NodeType) { case ExpressionType.AndAlso: case ExpressionType.And: return string.Format("({0} AND {1})", ParseTypedJoinCondition(binary.Left, leftPrefix, rightPrefix, leftMapper, rightMapper, leftParameter, rightParameter), ParseTypedJoinCondition(binary.Right, leftPrefix, rightPrefix, leftMapper, rightMapper, leftParameter, rightParameter)); case ExpressionType.OrElse: case ExpressionType.Or: return string.Format("({0} OR {1})", ParseTypedJoinCondition(binary.Left, leftPrefix, rightPrefix, leftMapper, rightMapper, leftParameter, rightParameter), ParseTypedJoinCondition(binary.Right, leftPrefix, rightPrefix, leftMapper, rightMapper, leftParameter, rightParameter)); case ExpressionType.Equal: case ExpressionType.NotEqual: case ExpressionType.GreaterThan: case ExpressionType.GreaterThanOrEqual: case ExpressionType.LessThan: case ExpressionType.LessThanOrEqual: { string left = ParseTypedJoinValue(binary.Left, leftPrefix, rightPrefix, leftMapper, rightMapper, leftParameter, rightParameter); string right = ParseTypedJoinValue(binary.Right, leftPrefix, rightPrefix, leftMapper, rightMapper, leftParameter, rightParameter); string op = GetBinaryOperator(binary.NodeType, IsNullConstant(binary.Right)); return string.Format("({0} {1} {2})", left, op, right); } } } if (expression is UnaryExpression unary && unary.NodeType == ExpressionType.Not) return string.Format("(NOT {0})", ParseTypedJoinCondition(unary.Operand, leftPrefix, rightPrefix, leftMapper, rightMapper, leftParameter, rightParameter)); throw new NotSupportedException(string.Format("Typed join condition is not supported: {0}", expression)); } private string ParseTypedJoinValue(Expression expression, string leftPrefix, string rightPrefix, DynamicTypeMap leftMapper, DynamicTypeMap rightMapper, ParameterExpression leftParameter, ParameterExpression rightParameter) { expression = UnwrapConvert(expression); MemberExpression member = expression as MemberExpression; if (member != null && member.Expression is ParameterExpression parameter) { if (parameter == leftParameter) return ParseTypedJoinMemberByMapper(member, leftPrefix, leftMapper); if (parameter == rightParameter) return ParseTypedJoinMemberByMapper(member, rightPrefix, rightMapper); } DynamicSchemaColumn? col = null; object value = EvaluateExpression(expression); return ParseConstant(value, Parameters, col); } private string ParseTypedJoinMember(Expression expression, string tablePrefix, DynamicTypeMap mapper) { expression = UnwrapConvert(expression); MemberExpression member = expression as MemberExpression; if (member == null || !(member.Expression is ParameterExpression) || ((ParameterExpression)member.Expression).Type != typeof(TModel)) throw new NotSupportedException(string.Format("Typed join member access is not supported: {0}", expression)); string mappedColumn = null; PropertyInfo property = member.Member as PropertyInfo; if (property != null) { var attrs = property.GetCustomAttributes(typeof(ColumnAttribute), true); ColumnAttribute colAttr = attrs == null ? null : attrs.Cast().FirstOrDefault(); if (colAttr != null && !string.IsNullOrEmpty(colAttr.Name)) mappedColumn = colAttr.Name; } if (string.IsNullOrEmpty(mappedColumn)) mappedColumn = mapper.PropertyMap.TryGetValue(member.Member.Name) ?? mapper.PropertyMap .Where(x => string.Equals(x.Key, member.Member.Name, StringComparison.OrdinalIgnoreCase)) .Select(x => x.Value) .FirstOrDefault() ?? member.Member.Name; return string.Format("{0}.{1}", tablePrefix, Database.DecorateName(mappedColumn)); } private string ParseTypedJoinMemberByMapper(MemberExpression member, string tablePrefix, DynamicTypeMap mapper) { string mappedColumn = null; PropertyInfo property = member.Member as PropertyInfo; if (property != null) { var attrs = property.GetCustomAttributes(typeof(ColumnAttribute), true); ColumnAttribute colAttr = attrs == null ? null : attrs.Cast().FirstOrDefault(); if (colAttr != null && !string.IsNullOrEmpty(colAttr.Name)) mappedColumn = colAttr.Name; } if (string.IsNullOrEmpty(mappedColumn)) mappedColumn = mapper.PropertyMap.TryGetValue(member.Member.Name) ?? mapper.PropertyMap .Where(x => string.Equals(x.Key, member.Member.Name, StringComparison.OrdinalIgnoreCase)) .Select(x => x.Value) .FirstOrDefault() ?? member.Member.Name; return string.Format("{0}.{1}", tablePrefix, Database.DecorateName(mappedColumn)); } private static Expression UnwrapConvert(Expression expression) { while (expression is UnaryExpression unary && (unary.NodeType == ExpressionType.Convert || unary.NodeType == ExpressionType.ConvertChecked)) expression = unary.Operand; return expression; } private static bool IsNullConstant(Expression expression) { expression = UnwrapConvert(expression); return expression is ConstantExpression constant && constant.Value == null; } private static string GetBinaryOperator(ExpressionType type, bool rightIsNull) { switch (type) { case ExpressionType.Equal: return rightIsNull ? "IS" : "="; case ExpressionType.NotEqual: return rightIsNull ? "IS NOT" : "<>"; case ExpressionType.GreaterThan: return ">"; case ExpressionType.GreaterThanOrEqual: return ">="; case ExpressionType.LessThan: return "<"; case ExpressionType.LessThanOrEqual: return "<="; default: throw new NotSupportedException(string.Format("Expression operation is not supported: {0}", type)); } } private static bool IsEnumerableContains(MethodCallExpression call) { if (!string.Equals(call.Method.Name, "Contains", StringComparison.Ordinal)) return false; if (call.Object != null && call.Arguments.Count == 1) return true; return call.Object == null && call.Arguments.Count == 2; } private static bool IsAsCall(MethodCallExpression call) { return string.Equals(call.Method.Name, "As", StringComparison.Ordinal) && (call.Arguments.Count == 1 || call.Arguments.Count == 2); } private static bool IsAscOrDesc(MethodCallExpression call) { string name = call.Method.Name.ToUpper(); return (name == "ASC" || name == "DESC") && (call.Arguments.Count == 0 || call.Arguments.Count == 1); } private static object EvaluateExpression(Expression expression) { var objectMember = Expression.Convert(expression, typeof(object)); var getter = Expression.Lambda>(objectMember).Compile(); return getter(); } } internal abstract class DynamicTypedSelectScopeQueryBuilderBase : IDynamicSelectQueryBuilder { protected readonly DynamicTypedSelectQueryBuilder Builder; protected DynamicTypedSelectScopeQueryBuilderBase(DynamicTypedSelectQueryBuilder builder) { Builder = builder; } public DynamicDatabase Database { get { return Builder.Database; } } public IList Tables { get { return Builder.Tables; } } public IDictionary Parameters { get { return Builder.Parameters; } } public bool VirtualMode { get { return Builder.VirtualMode; } set { Builder.VirtualMode = value; } } public bool SupportSchema { get { return Builder.SupportSchema; } } public List> OnCreateTemporaryParameter { get { return Builder.OnCreateTemporaryParameter; } set { Builder.OnCreateTemporaryParameter = value; } } public List> OnCreateParameter { get { return Builder.OnCreateParameter; } set { Builder.OnCreateParameter = value; } } public bool IsDisposed { get { return Builder.IsDisposed; } } public void Dispose() { Builder.Dispose(); } public IDbCommand FillCommand(IDbCommand command) { return Builder.FillCommand(command); } public string CommandText() { return Builder.CommandText(); } public IEnumerable Execute() { return Builder.Execute(); } public IEnumerable Execute() where T : class { return Builder.Execute(); } public void ExecuteDataReader(Action reader) { Builder.ExecuteDataReader(reader); } public object Scalar() { return Builder.Scalar(); } #if !DYNAMORM_OMMIT_GENERICEXECUTION && !DYNAMORM_OMMIT_TRYPARSE public TResult ScalarAs(TResult defaultValue = default(TResult)) { return Builder.ScalarAs(defaultValue); } #endif public IDynamicSelectQueryBuilder From(Func fn, params Func[] func) { Builder.From(fn, func); return this; } public IDynamicSelectQueryBuilder Join(params Func[] func) { Builder.Join(func); return this; } public IDynamicSelectQueryBuilder Where(Func func) { Builder.Where(func); return this; } public IDynamicSelectQueryBuilder Where(DynamicColumn column) { Builder.Where(column); return this; } public IDynamicSelectQueryBuilder Where(string column, DynamicColumn.CompareOperator op, object value) { Builder.Where(column, op, value); return this; } public IDynamicSelectQueryBuilder Where(string column, object value) { Builder.Where(column, value); return this; } public IDynamicSelectQueryBuilder Where(object conditions, bool schema = false) { Builder.Where(conditions, schema); return this; } public IDynamicSelectQueryBuilder Select(Func fn, params Func[] func) { Builder.Select(fn, func); return this; } public IDynamicSelectQueryBuilder SelectColumn(params DynamicColumn[] columns) { Builder.SelectColumn(columns); return this; } public IDynamicSelectQueryBuilder SelectColumn(params string[] columns) { Builder.SelectColumn(columns); return this; } public IDynamicSelectQueryBuilder GroupBy(Func fn, params Func[] func) { Builder.GroupBy(fn, func); return this; } public IDynamicSelectQueryBuilder GroupByColumn(params DynamicColumn[] columns) { Builder.GroupByColumn(columns); return this; } public IDynamicSelectQueryBuilder GroupByColumn(params string[] columns) { Builder.GroupByColumn(columns); return this; } public IDynamicSelectQueryBuilder Having(Func func) { Builder.Having(func); return this; } public IDynamicSelectQueryBuilder Having(DynamicColumn column) { Builder.Having(column); return this; } public IDynamicSelectQueryBuilder Having(string column, DynamicColumn.CompareOperator op, object value) { Builder.Having(column, op, value); return this; } public IDynamicSelectQueryBuilder Having(string column, object value) { Builder.Having(column, value); return this; } public IDynamicSelectQueryBuilder Having(object conditions, bool schema = false) { Builder.Having(conditions, schema); return this; } public IDynamicSelectQueryBuilder OrderBy(Func fn, params Func[] func) { Builder.OrderBy(fn, func); return this; } public IDynamicSelectQueryBuilder OrderByColumn(params DynamicColumn[] columns) { Builder.OrderByColumn(columns); return this; } public IDynamicSelectQueryBuilder OrderByColumn(params string[] columns) { Builder.OrderByColumn(columns); return this; } public IDynamicSelectQueryBuilder Top(int? top) { Builder.Top(top); return this; } public IDynamicSelectQueryBuilder Limit(int? limit) { Builder.Limit(limit); return this; } public IDynamicSelectQueryBuilder Offset(int? offset) { Builder.Offset(offset); return this; } public IDynamicSelectQueryBuilder Distinct(bool distinct = true) { Builder.Distinct(distinct); return this; } protected TypedJoinBuilder ApplyJoinSpec(TypedJoinBuilder builder, string alias, string customJoinType, DynamicJoinType joinType, bool noLock, string condition) { if (!string.IsNullOrEmpty(alias)) builder.As(alias); if (!string.IsNullOrEmpty(customJoinType)) builder.Type(customJoinType); else { switch (joinType) { case DynamicJoinType.Join: builder.Join(); break; case DynamicJoinType.Left: builder.Left(); break; case DynamicJoinType.Right: builder.Right(); break; case DynamicJoinType.Full: builder.Full(); break; case DynamicJoinType.LeftOuter: builder.LeftOuter(); break; case DynamicJoinType.RightOuter: builder.RightOuter(); break; case DynamicJoinType.FullOuter: builder.FullOuter(); break; default: builder.Inner(); break; } } if (noLock) builder.NoLock(); if (!string.IsNullOrEmpty(condition)) builder.OnRaw(condition); return builder; } protected void AddSelect(TypedSqlSelectable item) { Builder.AddSelectSqlSelector(item); } protected void AddGroupBy(TypedSqlExpression item) { Builder.AddGroupBySqlSelector(item); } protected void AddOrderBy(TypedSqlOrderExpression item) { Builder.AddOrderBySqlSelector(item); } protected void AddWhere(TypedSqlPredicate predicate) { Builder.AppendSqlCondition(Builder.RenderScopeSqlPredicate(predicate), false); } protected void AddHaving(TypedSqlPredicate predicate) { Builder.AppendSqlCondition(Builder.RenderScopeSqlPredicate(predicate), true); } } internal sealed class DynamicTypedSelectScopeQueryBuilder : DynamicTypedSelectScopeQueryBuilderBase, IDynamicTypedSelectScopeQueryBuilder { private readonly string _alias1; internal DynamicTypedSelectScopeQueryBuilder(DynamicTypedSelectQueryBuilder builder, string alias1) : base(builder) { _alias1 = alias1; } public IDynamicTypedSelectScopeQueryBuilder Join(Func, TypedScopeJoinBuilder> specification) { if (specification == null) throw new ArgumentNullException("specification"); TypedScopeJoinBuilder spec = specification(new TypedScopeJoinBuilder()); if (spec == null) throw new ArgumentException("Join specification cannot resolve to null.", "specification"); string alias = string.IsNullOrEmpty(spec.Alias) ? "t2" : spec.Alias; string condition = spec.OnSqlPredicate == null ? spec.OnRawCondition : Builder.RenderScopeSqlPredicate(spec.OnSqlPredicate(new TypedTableContext(_alias1), new TypedTableContext(alias))); Builder.Join(j => ApplyJoinSpec(j, alias, spec.CustomJoinType, spec.JoinType, spec.UseNoLock, condition)); return new DynamicTypedSelectScopeQueryBuilder(Builder, _alias1, alias); } public IDynamicTypedSelectScopeQueryBuilder SelectSql(Func, TypedSqlSelectable> selector, params Func, TypedSqlSelectable>[] selectors) { if (selector == null) throw new ArgumentNullException("selector"); AddSelect(selector(new TypedTableContext(_alias1))); if (selectors != null) foreach (var item in selectors) AddSelect(item(new TypedTableContext(_alias1))); return this; } public IDynamicTypedSelectScopeQueryBuilder WhereSql(Func, TypedSqlPredicate> predicate) { if (predicate == null) throw new ArgumentNullException("predicate"); AddWhere(predicate(new TypedTableContext(_alias1))); return this; } public IDynamicTypedSelectScopeQueryBuilder HavingSql(Func, TypedSqlPredicate> predicate) { if (predicate == null) throw new ArgumentNullException("predicate"); AddHaving(predicate(new TypedTableContext(_alias1))); return this; } public IDynamicTypedSelectScopeQueryBuilder GroupBySql(Func, TypedSqlExpression> selector, params Func, TypedSqlExpression>[] selectors) { if (selector == null) throw new ArgumentNullException("selector"); AddGroupBy(selector(new TypedTableContext(_alias1))); if (selectors != null) foreach (var item in selectors) AddGroupBy(item(new TypedTableContext(_alias1))); return this; } public IDynamicTypedSelectScopeQueryBuilder OrderBySql(Func, TypedSqlOrderExpression> selector, params Func, TypedSqlOrderExpression>[] selectors) { if (selector == null) throw new ArgumentNullException("selector"); AddOrderBy(selector(new TypedTableContext(_alias1))); if (selectors != null) foreach (var item in selectors) AddOrderBy(item(new TypedTableContext(_alias1))); return this; } } internal sealed class DynamicTypedSelectScopeQueryBuilder : DynamicTypedSelectScopeQueryBuilderBase, IDynamicTypedSelectScopeQueryBuilder { private readonly string _alias1; private readonly string _alias2; internal DynamicTypedSelectScopeQueryBuilder(DynamicTypedSelectQueryBuilder builder, string alias1, string alias2) : base(builder) { _alias1 = alias1; _alias2 = alias2; } public IDynamicTypedSelectScopeQueryBuilder Join(Func, TypedScopeJoinBuilder> specification) { if (specification == null) throw new ArgumentNullException("specification"); TypedScopeJoinBuilder spec = specification(new TypedScopeJoinBuilder()); if (spec == null) throw new ArgumentException("Join specification cannot resolve to null.", "specification"); string alias = string.IsNullOrEmpty(spec.Alias) ? "t3" : spec.Alias; string condition = spec.OnSqlPredicate == null ? spec.OnRawCondition : Builder.RenderScopeSqlPredicate(spec.OnSqlPredicate(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(alias))); Builder.Join(j => ApplyJoinSpec(j, alias, spec.CustomJoinType, spec.JoinType, spec.UseNoLock, condition)); return new DynamicTypedSelectScopeQueryBuilder(Builder, _alias1, _alias2, alias); } public IDynamicTypedSelectScopeQueryBuilder SelectSql(Func, TypedTableContext, TypedSqlSelectable> selector, params Func, TypedTableContext, TypedSqlSelectable>[] selectors) { if (selector == null) throw new ArgumentNullException("selector"); AddSelect(selector(new TypedTableContext(_alias1), new TypedTableContext(_alias2))); if (selectors != null) foreach (var item in selectors) AddSelect(item(new TypedTableContext(_alias1), new TypedTableContext(_alias2))); return this; } public IDynamicTypedSelectScopeQueryBuilder WhereSql(Func, TypedTableContext, TypedSqlPredicate> predicate) { if (predicate == null) throw new ArgumentNullException("predicate"); AddWhere(predicate(new TypedTableContext(_alias1), new TypedTableContext(_alias2))); return this; } public IDynamicTypedSelectScopeQueryBuilder HavingSql(Func, TypedTableContext, TypedSqlPredicate> predicate) { if (predicate == null) throw new ArgumentNullException("predicate"); AddHaving(predicate(new TypedTableContext(_alias1), new TypedTableContext(_alias2))); return this; } public IDynamicTypedSelectScopeQueryBuilder GroupBySql(Func, TypedTableContext, TypedSqlExpression> selector, params Func, TypedTableContext, TypedSqlExpression>[] selectors) { if (selector == null) throw new ArgumentNullException("selector"); AddGroupBy(selector(new TypedTableContext(_alias1), new TypedTableContext(_alias2))); if (selectors != null) foreach (var item in selectors) AddGroupBy(item(new TypedTableContext(_alias1), new TypedTableContext(_alias2))); return this; } public IDynamicTypedSelectScopeQueryBuilder OrderBySql(Func, TypedTableContext, TypedSqlOrderExpression> selector, params Func, TypedTableContext, TypedSqlOrderExpression>[] selectors) { if (selector == null) throw new ArgumentNullException("selector"); AddOrderBy(selector(new TypedTableContext(_alias1), new TypedTableContext(_alias2))); if (selectors != null) foreach (var item in selectors) AddOrderBy(item(new TypedTableContext(_alias1), new TypedTableContext(_alias2))); return this; } } internal sealed class DynamicTypedSelectScopeQueryBuilder : DynamicTypedSelectScopeQueryBuilderBase, IDynamicTypedSelectScopeQueryBuilder { private readonly string _alias1; private readonly string _alias2; private readonly string _alias3; internal DynamicTypedSelectScopeQueryBuilder(DynamicTypedSelectQueryBuilder builder, string alias1, string alias2, string alias3) : base(builder) { _alias1 = alias1; _alias2 = alias2; _alias3 = alias3; } public IDynamicTypedSelectScopeQueryBuilder Join(Func, TypedScopeJoinBuilder> specification) { if (specification == null) throw new ArgumentNullException("specification"); TypedScopeJoinBuilder spec = specification(new TypedScopeJoinBuilder()); if (spec == null) throw new ArgumentException("Join specification cannot resolve to null.", "specification"); string alias = string.IsNullOrEmpty(spec.Alias) ? "t4" : spec.Alias; string condition = spec.OnSqlPredicate == null ? spec.OnRawCondition : Builder.RenderScopeSqlPredicate(spec.OnSqlPredicate(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(alias))); Builder.Join(j => ApplyJoinSpec(j, alias, spec.CustomJoinType, spec.JoinType, spec.UseNoLock, condition)); return new DynamicTypedSelectScopeQueryBuilder(Builder, _alias1, _alias2, _alias3, alias); } public IDynamicTypedSelectScopeQueryBuilder SelectSql(Func, TypedTableContext, TypedTableContext, TypedSqlSelectable> selector, params Func, TypedTableContext, TypedTableContext, TypedSqlSelectable>[] selectors) { if (selector == null) throw new ArgumentNullException("selector"); AddSelect(selector(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3))); if (selectors != null) foreach (var item in selectors) AddSelect(item(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3))); return this; } public IDynamicTypedSelectScopeQueryBuilder WhereSql(Func, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate) { if (predicate == null) throw new ArgumentNullException("predicate"); AddWhere(predicate(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3))); return this; } public IDynamicTypedSelectScopeQueryBuilder HavingSql(Func, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate) { if (predicate == null) throw new ArgumentNullException("predicate"); AddHaving(predicate(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3))); return this; } public IDynamicTypedSelectScopeQueryBuilder GroupBySql(Func, TypedTableContext, TypedTableContext, TypedSqlExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedSqlExpression>[] selectors) { if (selector == null) throw new ArgumentNullException("selector"); AddGroupBy(selector(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3))); if (selectors != null) foreach (var item in selectors) AddGroupBy(item(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3))); return this; } public IDynamicTypedSelectScopeQueryBuilder OrderBySql(Func, TypedTableContext, TypedTableContext, TypedSqlOrderExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedSqlOrderExpression>[] selectors) { if (selector == null) throw new ArgumentNullException("selector"); AddOrderBy(selector(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3))); if (selectors != null) foreach (var item in selectors) AddOrderBy(item(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3))); return this; } } internal sealed class DynamicTypedSelectScopeQueryBuilder : DynamicTypedSelectScopeQueryBuilderBase, IDynamicTypedSelectScopeQueryBuilder { private readonly string _alias1; private readonly string _alias2; private readonly string _alias3; private readonly string _alias4; internal DynamicTypedSelectScopeQueryBuilder(DynamicTypedSelectQueryBuilder builder, string alias1, string alias2, string alias3, string alias4) : base(builder) { _alias1 = alias1; _alias2 = alias2; _alias3 = alias3; _alias4 = alias4; } public IDynamicTypedSelectScopeQueryBuilder Join(Func, TypedScopeJoinBuilder> specification) { if (specification == null) throw new ArgumentNullException("specification"); TypedScopeJoinBuilder spec = specification(new TypedScopeJoinBuilder()); if (spec == null) throw new ArgumentException("Join specification cannot resolve to null.", "specification"); string alias = string.IsNullOrEmpty(spec.Alias) ? "t5" : spec.Alias; string condition = spec.OnSqlPredicate == null ? spec.OnRawCondition : Builder.RenderScopeSqlPredicate(spec.OnSqlPredicate(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(alias))); Builder.Join(j => ApplyJoinSpec(j, alias, spec.CustomJoinType, spec.JoinType, spec.UseNoLock, condition)); return new DynamicTypedSelectScopeQueryBuilder(Builder, _alias1, _alias2, _alias3, _alias4, alias); } public IDynamicTypedSelectScopeQueryBuilder SelectSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable>[] selectors) { if (selector == null) throw new ArgumentNullException("selector"); AddSelect(selector(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4))); if (selectors != null) foreach (var item in selectors) AddSelect(item(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4))); return this; } public IDynamicTypedSelectScopeQueryBuilder WhereSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate) { if (predicate == null) throw new ArgumentNullException("predicate"); AddWhere(predicate(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4))); return this; } public IDynamicTypedSelectScopeQueryBuilder HavingSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate) { if (predicate == null) throw new ArgumentNullException("predicate"); AddHaving(predicate(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4))); return this; } public IDynamicTypedSelectScopeQueryBuilder GroupBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression>[] selectors) { if (selector == null) throw new ArgumentNullException("selector"); AddGroupBy(selector(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4))); if (selectors != null) foreach (var item in selectors) AddGroupBy(item(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4))); return this; } public IDynamicTypedSelectScopeQueryBuilder OrderBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression>[] selectors) { if (selector == null) throw new ArgumentNullException("selector"); AddOrderBy(selector(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4))); if (selectors != null) foreach (var item in selectors) AddOrderBy(item(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4))); return this; } } internal sealed class DynamicTypedSelectScopeQueryBuilder : DynamicTypedSelectScopeQueryBuilderBase, IDynamicTypedSelectScopeQueryBuilder { private readonly string _alias1; private readonly string _alias2; private readonly string _alias3; private readonly string _alias4; private readonly string _alias5; internal DynamicTypedSelectScopeQueryBuilder(DynamicTypedSelectQueryBuilder builder, string alias1, string alias2, string alias3, string alias4, string alias5) : base(builder) { _alias1 = alias1; _alias2 = alias2; _alias3 = alias3; _alias4 = alias4; _alias5 = alias5; } public IDynamicTypedSelectScopeQueryBuilder Join(Func, TypedScopeJoinBuilder> specification) { if (specification == null) throw new ArgumentNullException("specification"); TypedScopeJoinBuilder spec = specification(new TypedScopeJoinBuilder()); if (spec == null) throw new ArgumentException("Join specification cannot resolve to null.", "specification"); string alias = string.IsNullOrEmpty(spec.Alias) ? "t6" : spec.Alias; string condition = spec.OnSqlPredicate == null ? spec.OnRawCondition : Builder.RenderScopeSqlPredicate(spec.OnSqlPredicate(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(alias))); Builder.Join(j => ApplyJoinSpec(j, alias, spec.CustomJoinType, spec.JoinType, spec.UseNoLock, condition)); return new DynamicTypedSelectScopeQueryBuilder(Builder, _alias1, _alias2, _alias3, _alias4, _alias5, alias); } public IDynamicTypedSelectScopeQueryBuilder SelectSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable>[] selectors) { if (selector == null) throw new ArgumentNullException("selector"); AddSelect(selector(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5))); if (selectors != null) foreach (var item in selectors) AddSelect(item(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5))); return this; } public IDynamicTypedSelectScopeQueryBuilder WhereSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate) { if (predicate == null) throw new ArgumentNullException("predicate"); AddWhere(predicate(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5))); return this; } public IDynamicTypedSelectScopeQueryBuilder HavingSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate) { if (predicate == null) throw new ArgumentNullException("predicate"); AddHaving(predicate(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5))); return this; } public IDynamicTypedSelectScopeQueryBuilder GroupBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression>[] selectors) { if (selector == null) throw new ArgumentNullException("selector"); AddGroupBy(selector(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5))); if (selectors != null) foreach (var item in selectors) AddGroupBy(item(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5))); return this; } public IDynamicTypedSelectScopeQueryBuilder OrderBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression>[] selectors) { if (selector == null) throw new ArgumentNullException("selector"); AddOrderBy(selector(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5))); if (selectors != null) foreach (var item in selectors) AddOrderBy(item(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5))); return this; } } internal sealed class DynamicTypedSelectScopeQueryBuilder : DynamicTypedSelectScopeQueryBuilderBase, IDynamicTypedSelectScopeQueryBuilder { private readonly string _alias1; private readonly string _alias2; private readonly string _alias3; private readonly string _alias4; private readonly string _alias5; private readonly string _alias6; internal DynamicTypedSelectScopeQueryBuilder(DynamicTypedSelectQueryBuilder builder, string alias1, string alias2, string alias3, string alias4, string alias5, string alias6) : base(builder) { _alias1 = alias1; _alias2 = alias2; _alias3 = alias3; _alias4 = alias4; _alias5 = alias5; _alias6 = alias6; } public IDynamicTypedSelectScopeQueryBuilder Join(Func, TypedScopeJoinBuilder> specification) { if (specification == null) throw new ArgumentNullException("specification"); TypedScopeJoinBuilder spec = specification(new TypedScopeJoinBuilder()); if (spec == null) throw new ArgumentException("Join specification cannot resolve to null.", "specification"); string alias = string.IsNullOrEmpty(spec.Alias) ? "t7" : spec.Alias; string condition = spec.OnSqlPredicate == null ? spec.OnRawCondition : Builder.RenderScopeSqlPredicate(spec.OnSqlPredicate(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(alias))); Builder.Join(j => ApplyJoinSpec(j, alias, spec.CustomJoinType, spec.JoinType, spec.UseNoLock, condition)); return new DynamicTypedSelectScopeQueryBuilder(Builder, _alias1, _alias2, _alias3, _alias4, _alias5, _alias6, alias); } public IDynamicTypedSelectScopeQueryBuilder SelectSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable>[] selectors) { if (selector == null) throw new ArgumentNullException("selector"); AddSelect(selector(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6))); if (selectors != null) foreach (var item in selectors) AddSelect(item(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6))); return this; } public IDynamicTypedSelectScopeQueryBuilder WhereSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate) { if (predicate == null) throw new ArgumentNullException("predicate"); AddWhere(predicate(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6))); return this; } public IDynamicTypedSelectScopeQueryBuilder HavingSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate) { if (predicate == null) throw new ArgumentNullException("predicate"); AddHaving(predicate(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6))); return this; } public IDynamicTypedSelectScopeQueryBuilder GroupBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression>[] selectors) { if (selector == null) throw new ArgumentNullException("selector"); AddGroupBy(selector(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6))); if (selectors != null) foreach (var item in selectors) AddGroupBy(item(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6))); return this; } public IDynamicTypedSelectScopeQueryBuilder OrderBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression>[] selectors) { if (selector == null) throw new ArgumentNullException("selector"); AddOrderBy(selector(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6))); if (selectors != null) foreach (var item in selectors) AddOrderBy(item(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6))); return this; } } internal sealed class DynamicTypedSelectScopeQueryBuilder : DynamicTypedSelectScopeQueryBuilderBase, IDynamicTypedSelectScopeQueryBuilder { private readonly string _alias1; private readonly string _alias2; private readonly string _alias3; private readonly string _alias4; private readonly string _alias5; private readonly string _alias6; private readonly string _alias7; internal DynamicTypedSelectScopeQueryBuilder(DynamicTypedSelectQueryBuilder builder, string alias1, string alias2, string alias3, string alias4, string alias5, string alias6, string alias7) : base(builder) { _alias1 = alias1; _alias2 = alias2; _alias3 = alias3; _alias4 = alias4; _alias5 = alias5; _alias6 = alias6; _alias7 = alias7; } public IDynamicTypedSelectScopeQueryBuilder Join(Func, TypedScopeJoinBuilder> specification) { if (specification == null) throw new ArgumentNullException("specification"); TypedScopeJoinBuilder spec = specification(new TypedScopeJoinBuilder()); if (spec == null) throw new ArgumentException("Join specification cannot resolve to null.", "specification"); string alias = string.IsNullOrEmpty(spec.Alias) ? "t8" : spec.Alias; string condition = spec.OnSqlPredicate == null ? spec.OnRawCondition : Builder.RenderScopeSqlPredicate(spec.OnSqlPredicate(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(alias))); Builder.Join(j => ApplyJoinSpec(j, alias, spec.CustomJoinType, spec.JoinType, spec.UseNoLock, condition)); return new DynamicTypedSelectScopeQueryBuilder(Builder, _alias1, _alias2, _alias3, _alias4, _alias5, _alias6, _alias7, alias); } public IDynamicTypedSelectScopeQueryBuilder SelectSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable>[] selectors) { if (selector == null) throw new ArgumentNullException("selector"); AddSelect(selector(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7))); if (selectors != null) foreach (var item in selectors) AddSelect(item(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7))); return this; } public IDynamicTypedSelectScopeQueryBuilder WhereSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate) { if (predicate == null) throw new ArgumentNullException("predicate"); AddWhere(predicate(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7))); return this; } public IDynamicTypedSelectScopeQueryBuilder HavingSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate) { if (predicate == null) throw new ArgumentNullException("predicate"); AddHaving(predicate(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7))); return this; } public IDynamicTypedSelectScopeQueryBuilder GroupBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression>[] selectors) { if (selector == null) throw new ArgumentNullException("selector"); AddGroupBy(selector(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7))); if (selectors != null) foreach (var item in selectors) AddGroupBy(item(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7))); return this; } public IDynamicTypedSelectScopeQueryBuilder OrderBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression>[] selectors) { if (selector == null) throw new ArgumentNullException("selector"); AddOrderBy(selector(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7))); if (selectors != null) foreach (var item in selectors) AddOrderBy(item(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7))); return this; } } internal sealed class DynamicTypedSelectScopeQueryBuilder : DynamicTypedSelectScopeQueryBuilderBase, IDynamicTypedSelectScopeQueryBuilder { private readonly string _alias1; private readonly string _alias2; private readonly string _alias3; private readonly string _alias4; private readonly string _alias5; private readonly string _alias6; private readonly string _alias7; private readonly string _alias8; internal DynamicTypedSelectScopeQueryBuilder(DynamicTypedSelectQueryBuilder builder, string alias1, string alias2, string alias3, string alias4, string alias5, string alias6, string alias7, string alias8) : base(builder) { _alias1 = alias1; _alias2 = alias2; _alias3 = alias3; _alias4 = alias4; _alias5 = alias5; _alias6 = alias6; _alias7 = alias7; _alias8 = alias8; } public IDynamicTypedSelectScopeQueryBuilder Join(Func, TypedScopeJoinBuilder> specification) { if (specification == null) throw new ArgumentNullException("specification"); TypedScopeJoinBuilder spec = specification(new TypedScopeJoinBuilder()); if (spec == null) throw new ArgumentException("Join specification cannot resolve to null.", "specification"); string alias = string.IsNullOrEmpty(spec.Alias) ? "t9" : spec.Alias; string condition = spec.OnSqlPredicate == null ? spec.OnRawCondition : Builder.RenderScopeSqlPredicate(spec.OnSqlPredicate(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(alias))); Builder.Join(j => ApplyJoinSpec(j, alias, spec.CustomJoinType, spec.JoinType, spec.UseNoLock, condition)); return new DynamicTypedSelectScopeQueryBuilder(Builder, _alias1, _alias2, _alias3, _alias4, _alias5, _alias6, _alias7, _alias8, alias); } public IDynamicTypedSelectScopeQueryBuilder SelectSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable>[] selectors) { if (selector == null) throw new ArgumentNullException("selector"); AddSelect(selector(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8))); if (selectors != null) foreach (var item in selectors) AddSelect(item(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8))); return this; } public IDynamicTypedSelectScopeQueryBuilder WhereSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate) { if (predicate == null) throw new ArgumentNullException("predicate"); AddWhere(predicate(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8))); return this; } public IDynamicTypedSelectScopeQueryBuilder HavingSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate) { if (predicate == null) throw new ArgumentNullException("predicate"); AddHaving(predicate(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8))); return this; } public IDynamicTypedSelectScopeQueryBuilder GroupBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression>[] selectors) { if (selector == null) throw new ArgumentNullException("selector"); AddGroupBy(selector(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8))); if (selectors != null) foreach (var item in selectors) AddGroupBy(item(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8))); return this; } public IDynamicTypedSelectScopeQueryBuilder OrderBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression>[] selectors) { if (selector == null) throw new ArgumentNullException("selector"); AddOrderBy(selector(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8))); if (selectors != null) foreach (var item in selectors) AddOrderBy(item(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8))); return this; } } internal sealed class DynamicTypedSelectScopeQueryBuilder : DynamicTypedSelectScopeQueryBuilderBase, IDynamicTypedSelectScopeQueryBuilder { private readonly string _alias1; private readonly string _alias2; private readonly string _alias3; private readonly string _alias4; private readonly string _alias5; private readonly string _alias6; private readonly string _alias7; private readonly string _alias8; private readonly string _alias9; internal DynamicTypedSelectScopeQueryBuilder(DynamicTypedSelectQueryBuilder builder, string alias1, string alias2, string alias3, string alias4, string alias5, string alias6, string alias7, string alias8, string alias9) : base(builder) { _alias1 = alias1; _alias2 = alias2; _alias3 = alias3; _alias4 = alias4; _alias5 = alias5; _alias6 = alias6; _alias7 = alias7; _alias8 = alias8; _alias9 = alias9; } public IDynamicTypedSelectScopeQueryBuilder Join(Func, TypedScopeJoinBuilder> specification) { if (specification == null) throw new ArgumentNullException("specification"); TypedScopeJoinBuilder spec = specification(new TypedScopeJoinBuilder()); if (spec == null) throw new ArgumentException("Join specification cannot resolve to null.", "specification"); string alias = string.IsNullOrEmpty(spec.Alias) ? "t10" : spec.Alias; string condition = spec.OnSqlPredicate == null ? spec.OnRawCondition : Builder.RenderScopeSqlPredicate(spec.OnSqlPredicate(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9), new TypedTableContext(alias))); Builder.Join(j => ApplyJoinSpec(j, alias, spec.CustomJoinType, spec.JoinType, spec.UseNoLock, condition)); return new DynamicTypedSelectScopeQueryBuilder(Builder, _alias1, _alias2, _alias3, _alias4, _alias5, _alias6, _alias7, _alias8, _alias9, alias); } public IDynamicTypedSelectScopeQueryBuilder SelectSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable>[] selectors) { if (selector == null) throw new ArgumentNullException("selector"); AddSelect(selector(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9))); if (selectors != null) foreach (var item in selectors) AddSelect(item(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9))); return this; } public IDynamicTypedSelectScopeQueryBuilder WhereSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate) { if (predicate == null) throw new ArgumentNullException("predicate"); AddWhere(predicate(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9))); return this; } public IDynamicTypedSelectScopeQueryBuilder HavingSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate) { if (predicate == null) throw new ArgumentNullException("predicate"); AddHaving(predicate(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9))); return this; } public IDynamicTypedSelectScopeQueryBuilder GroupBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression>[] selectors) { if (selector == null) throw new ArgumentNullException("selector"); AddGroupBy(selector(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9))); if (selectors != null) foreach (var item in selectors) AddGroupBy(item(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9))); return this; } public IDynamicTypedSelectScopeQueryBuilder OrderBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression>[] selectors) { if (selector == null) throw new ArgumentNullException("selector"); AddOrderBy(selector(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9))); if (selectors != null) foreach (var item in selectors) AddOrderBy(item(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9))); return this; } } internal sealed class DynamicTypedSelectScopeQueryBuilder : DynamicTypedSelectScopeQueryBuilderBase, IDynamicTypedSelectScopeQueryBuilder { private readonly string _alias1; private readonly string _alias2; private readonly string _alias3; private readonly string _alias4; private readonly string _alias5; private readonly string _alias6; private readonly string _alias7; private readonly string _alias8; private readonly string _alias9; private readonly string _alias10; internal DynamicTypedSelectScopeQueryBuilder(DynamicTypedSelectQueryBuilder builder, string alias1, string alias2, string alias3, string alias4, string alias5, string alias6, string alias7, string alias8, string alias9, string alias10) : base(builder) { _alias1 = alias1; _alias2 = alias2; _alias3 = alias3; _alias4 = alias4; _alias5 = alias5; _alias6 = alias6; _alias7 = alias7; _alias8 = alias8; _alias9 = alias9; _alias10 = alias10; } public IDynamicTypedSelectScopeQueryBuilder Join(Func, TypedScopeJoinBuilder> specification) { if (specification == null) throw new ArgumentNullException("specification"); TypedScopeJoinBuilder spec = specification(new TypedScopeJoinBuilder()); if (spec == null) throw new ArgumentException("Join specification cannot resolve to null.", "specification"); string alias = string.IsNullOrEmpty(spec.Alias) ? "t11" : spec.Alias; string condition = spec.OnSqlPredicate == null ? spec.OnRawCondition : Builder.RenderScopeSqlPredicate(spec.OnSqlPredicate(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9), new TypedTableContext(_alias10), new TypedTableContext(alias))); Builder.Join(j => ApplyJoinSpec(j, alias, spec.CustomJoinType, spec.JoinType, spec.UseNoLock, condition)); return new DynamicTypedSelectScopeQueryBuilder(Builder, _alias1, _alias2, _alias3, _alias4, _alias5, _alias6, _alias7, _alias8, _alias9, _alias10, alias); } public IDynamicTypedSelectScopeQueryBuilder SelectSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable>[] selectors) { if (selector == null) throw new ArgumentNullException("selector"); AddSelect(selector(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9), new TypedTableContext(_alias10))); if (selectors != null) foreach (var item in selectors) AddSelect(item(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9), new TypedTableContext(_alias10))); return this; } public IDynamicTypedSelectScopeQueryBuilder WhereSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate) { if (predicate == null) throw new ArgumentNullException("predicate"); AddWhere(predicate(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9), new TypedTableContext(_alias10))); return this; } public IDynamicTypedSelectScopeQueryBuilder HavingSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate) { if (predicate == null) throw new ArgumentNullException("predicate"); AddHaving(predicate(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9), new TypedTableContext(_alias10))); return this; } public IDynamicTypedSelectScopeQueryBuilder GroupBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression>[] selectors) { if (selector == null) throw new ArgumentNullException("selector"); AddGroupBy(selector(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9), new TypedTableContext(_alias10))); if (selectors != null) foreach (var item in selectors) AddGroupBy(item(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9), new TypedTableContext(_alias10))); return this; } public IDynamicTypedSelectScopeQueryBuilder OrderBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression>[] selectors) { if (selector == null) throw new ArgumentNullException("selector"); AddOrderBy(selector(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9), new TypedTableContext(_alias10))); if (selectors != null) foreach (var item in selectors) AddOrderBy(item(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9), new TypedTableContext(_alias10))); return this; } } internal sealed class DynamicTypedSelectScopeQueryBuilder : DynamicTypedSelectScopeQueryBuilderBase, IDynamicTypedSelectScopeQueryBuilder { private readonly string _alias1; private readonly string _alias2; private readonly string _alias3; private readonly string _alias4; private readonly string _alias5; private readonly string _alias6; private readonly string _alias7; private readonly string _alias8; private readonly string _alias9; private readonly string _alias10; private readonly string _alias11; internal DynamicTypedSelectScopeQueryBuilder(DynamicTypedSelectQueryBuilder builder, string alias1, string alias2, string alias3, string alias4, string alias5, string alias6, string alias7, string alias8, string alias9, string alias10, string alias11) : base(builder) { _alias1 = alias1; _alias2 = alias2; _alias3 = alias3; _alias4 = alias4; _alias5 = alias5; _alias6 = alias6; _alias7 = alias7; _alias8 = alias8; _alias9 = alias9; _alias10 = alias10; _alias11 = alias11; } public IDynamicTypedSelectScopeQueryBuilder Join(Func, TypedScopeJoinBuilder> specification) { if (specification == null) throw new ArgumentNullException("specification"); TypedScopeJoinBuilder spec = specification(new TypedScopeJoinBuilder()); if (spec == null) throw new ArgumentException("Join specification cannot resolve to null.", "specification"); string alias = string.IsNullOrEmpty(spec.Alias) ? "t12" : spec.Alias; string condition = spec.OnSqlPredicate == null ? spec.OnRawCondition : Builder.RenderScopeSqlPredicate(spec.OnSqlPredicate(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9), new TypedTableContext(_alias10), new TypedTableContext(_alias11), new TypedTableContext(alias))); Builder.Join(j => ApplyJoinSpec(j, alias, spec.CustomJoinType, spec.JoinType, spec.UseNoLock, condition)); return new DynamicTypedSelectScopeQueryBuilder(Builder, _alias1, _alias2, _alias3, _alias4, _alias5, _alias6, _alias7, _alias8, _alias9, _alias10, _alias11, alias); } public IDynamicTypedSelectScopeQueryBuilder SelectSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable>[] selectors) { if (selector == null) throw new ArgumentNullException("selector"); AddSelect(selector(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9), new TypedTableContext(_alias10), new TypedTableContext(_alias11))); if (selectors != null) foreach (var item in selectors) AddSelect(item(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9), new TypedTableContext(_alias10), new TypedTableContext(_alias11))); return this; } public IDynamicTypedSelectScopeQueryBuilder WhereSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate) { if (predicate == null) throw new ArgumentNullException("predicate"); AddWhere(predicate(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9), new TypedTableContext(_alias10), new TypedTableContext(_alias11))); return this; } public IDynamicTypedSelectScopeQueryBuilder HavingSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate) { if (predicate == null) throw new ArgumentNullException("predicate"); AddHaving(predicate(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9), new TypedTableContext(_alias10), new TypedTableContext(_alias11))); return this; } public IDynamicTypedSelectScopeQueryBuilder GroupBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression>[] selectors) { if (selector == null) throw new ArgumentNullException("selector"); AddGroupBy(selector(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9), new TypedTableContext(_alias10), new TypedTableContext(_alias11))); if (selectors != null) foreach (var item in selectors) AddGroupBy(item(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9), new TypedTableContext(_alias10), new TypedTableContext(_alias11))); return this; } public IDynamicTypedSelectScopeQueryBuilder OrderBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression>[] selectors) { if (selector == null) throw new ArgumentNullException("selector"); AddOrderBy(selector(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9), new TypedTableContext(_alias10), new TypedTableContext(_alias11))); if (selectors != null) foreach (var item in selectors) AddOrderBy(item(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9), new TypedTableContext(_alias10), new TypedTableContext(_alias11))); return this; } } internal sealed class DynamicTypedSelectScopeQueryBuilder : DynamicTypedSelectScopeQueryBuilderBase, IDynamicTypedSelectScopeQueryBuilder { private readonly string _alias1; private readonly string _alias2; private readonly string _alias3; private readonly string _alias4; private readonly string _alias5; private readonly string _alias6; private readonly string _alias7; private readonly string _alias8; private readonly string _alias9; private readonly string _alias10; private readonly string _alias11; private readonly string _alias12; internal DynamicTypedSelectScopeQueryBuilder(DynamicTypedSelectQueryBuilder builder, string alias1, string alias2, string alias3, string alias4, string alias5, string alias6, string alias7, string alias8, string alias9, string alias10, string alias11, string alias12) : base(builder) { _alias1 = alias1; _alias2 = alias2; _alias3 = alias3; _alias4 = alias4; _alias5 = alias5; _alias6 = alias6; _alias7 = alias7; _alias8 = alias8; _alias9 = alias9; _alias10 = alias10; _alias11 = alias11; _alias12 = alias12; } public IDynamicTypedSelectScopeQueryBuilder Join(Func, TypedScopeJoinBuilder> specification) { if (specification == null) throw new ArgumentNullException("specification"); TypedScopeJoinBuilder spec = specification(new TypedScopeJoinBuilder()); if (spec == null) throw new ArgumentException("Join specification cannot resolve to null.", "specification"); string alias = string.IsNullOrEmpty(spec.Alias) ? "t13" : spec.Alias; string condition = spec.OnSqlPredicate == null ? spec.OnRawCondition : Builder.RenderScopeSqlPredicate(spec.OnSqlPredicate(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9), new TypedTableContext(_alias10), new TypedTableContext(_alias11), new TypedTableContext(_alias12), new TypedTableContext(alias))); Builder.Join(j => ApplyJoinSpec(j, alias, spec.CustomJoinType, spec.JoinType, spec.UseNoLock, condition)); return new DynamicTypedSelectScopeQueryBuilder(Builder, _alias1, _alias2, _alias3, _alias4, _alias5, _alias6, _alias7, _alias8, _alias9, _alias10, _alias11, _alias12, alias); } public IDynamicTypedSelectScopeQueryBuilder SelectSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable>[] selectors) { if (selector == null) throw new ArgumentNullException("selector"); AddSelect(selector(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9), new TypedTableContext(_alias10), new TypedTableContext(_alias11), new TypedTableContext(_alias12))); if (selectors != null) foreach (var item in selectors) AddSelect(item(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9), new TypedTableContext(_alias10), new TypedTableContext(_alias11), new TypedTableContext(_alias12))); return this; } public IDynamicTypedSelectScopeQueryBuilder WhereSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate) { if (predicate == null) throw new ArgumentNullException("predicate"); AddWhere(predicate(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9), new TypedTableContext(_alias10), new TypedTableContext(_alias11), new TypedTableContext(_alias12))); return this; } public IDynamicTypedSelectScopeQueryBuilder HavingSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate) { if (predicate == null) throw new ArgumentNullException("predicate"); AddHaving(predicate(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9), new TypedTableContext(_alias10), new TypedTableContext(_alias11), new TypedTableContext(_alias12))); return this; } public IDynamicTypedSelectScopeQueryBuilder GroupBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression>[] selectors) { if (selector == null) throw new ArgumentNullException("selector"); AddGroupBy(selector(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9), new TypedTableContext(_alias10), new TypedTableContext(_alias11), new TypedTableContext(_alias12))); if (selectors != null) foreach (var item in selectors) AddGroupBy(item(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9), new TypedTableContext(_alias10), new TypedTableContext(_alias11), new TypedTableContext(_alias12))); return this; } public IDynamicTypedSelectScopeQueryBuilder OrderBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression>[] selectors) { if (selector == null) throw new ArgumentNullException("selector"); AddOrderBy(selector(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9), new TypedTableContext(_alias10), new TypedTableContext(_alias11), new TypedTableContext(_alias12))); if (selectors != null) foreach (var item in selectors) AddOrderBy(item(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9), new TypedTableContext(_alias10), new TypedTableContext(_alias11), new TypedTableContext(_alias12))); return this; } } internal sealed class DynamicTypedSelectScopeQueryBuilder : DynamicTypedSelectScopeQueryBuilderBase, IDynamicTypedSelectScopeQueryBuilder { private readonly string _alias1; private readonly string _alias2; private readonly string _alias3; private readonly string _alias4; private readonly string _alias5; private readonly string _alias6; private readonly string _alias7; private readonly string _alias8; private readonly string _alias9; private readonly string _alias10; private readonly string _alias11; private readonly string _alias12; private readonly string _alias13; internal DynamicTypedSelectScopeQueryBuilder(DynamicTypedSelectQueryBuilder builder, string alias1, string alias2, string alias3, string alias4, string alias5, string alias6, string alias7, string alias8, string alias9, string alias10, string alias11, string alias12, string alias13) : base(builder) { _alias1 = alias1; _alias2 = alias2; _alias3 = alias3; _alias4 = alias4; _alias5 = alias5; _alias6 = alias6; _alias7 = alias7; _alias8 = alias8; _alias9 = alias9; _alias10 = alias10; _alias11 = alias11; _alias12 = alias12; _alias13 = alias13; } public IDynamicTypedSelectScopeQueryBuilder Join(Func, TypedScopeJoinBuilder> specification) { if (specification == null) throw new ArgumentNullException("specification"); TypedScopeJoinBuilder spec = specification(new TypedScopeJoinBuilder()); if (spec == null) throw new ArgumentException("Join specification cannot resolve to null.", "specification"); string alias = string.IsNullOrEmpty(spec.Alias) ? "t14" : spec.Alias; string condition = spec.OnSqlPredicate == null ? spec.OnRawCondition : Builder.RenderScopeSqlPredicate(spec.OnSqlPredicate(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9), new TypedTableContext(_alias10), new TypedTableContext(_alias11), new TypedTableContext(_alias12), new TypedTableContext(_alias13), new TypedTableContext(alias))); Builder.Join(j => ApplyJoinSpec(j, alias, spec.CustomJoinType, spec.JoinType, spec.UseNoLock, condition)); return new DynamicTypedSelectScopeQueryBuilder(Builder, _alias1, _alias2, _alias3, _alias4, _alias5, _alias6, _alias7, _alias8, _alias9, _alias10, _alias11, _alias12, _alias13, alias); } public IDynamicTypedSelectScopeQueryBuilder SelectSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable>[] selectors) { if (selector == null) throw new ArgumentNullException("selector"); AddSelect(selector(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9), new TypedTableContext(_alias10), new TypedTableContext(_alias11), new TypedTableContext(_alias12), new TypedTableContext(_alias13))); if (selectors != null) foreach (var item in selectors) AddSelect(item(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9), new TypedTableContext(_alias10), new TypedTableContext(_alias11), new TypedTableContext(_alias12), new TypedTableContext(_alias13))); return this; } public IDynamicTypedSelectScopeQueryBuilder WhereSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate) { if (predicate == null) throw new ArgumentNullException("predicate"); AddWhere(predicate(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9), new TypedTableContext(_alias10), new TypedTableContext(_alias11), new TypedTableContext(_alias12), new TypedTableContext(_alias13))); return this; } public IDynamicTypedSelectScopeQueryBuilder HavingSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate) { if (predicate == null) throw new ArgumentNullException("predicate"); AddHaving(predicate(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9), new TypedTableContext(_alias10), new TypedTableContext(_alias11), new TypedTableContext(_alias12), new TypedTableContext(_alias13))); return this; } public IDynamicTypedSelectScopeQueryBuilder GroupBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression>[] selectors) { if (selector == null) throw new ArgumentNullException("selector"); AddGroupBy(selector(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9), new TypedTableContext(_alias10), new TypedTableContext(_alias11), new TypedTableContext(_alias12), new TypedTableContext(_alias13))); if (selectors != null) foreach (var item in selectors) AddGroupBy(item(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9), new TypedTableContext(_alias10), new TypedTableContext(_alias11), new TypedTableContext(_alias12), new TypedTableContext(_alias13))); return this; } public IDynamicTypedSelectScopeQueryBuilder OrderBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression>[] selectors) { if (selector == null) throw new ArgumentNullException("selector"); AddOrderBy(selector(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9), new TypedTableContext(_alias10), new TypedTableContext(_alias11), new TypedTableContext(_alias12), new TypedTableContext(_alias13))); if (selectors != null) foreach (var item in selectors) AddOrderBy(item(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9), new TypedTableContext(_alias10), new TypedTableContext(_alias11), new TypedTableContext(_alias12), new TypedTableContext(_alias13))); return this; } } internal sealed class DynamicTypedSelectScopeQueryBuilder : DynamicTypedSelectScopeQueryBuilderBase, IDynamicTypedSelectScopeQueryBuilder { private readonly string _alias1; private readonly string _alias2; private readonly string _alias3; private readonly string _alias4; private readonly string _alias5; private readonly string _alias6; private readonly string _alias7; private readonly string _alias8; private readonly string _alias9; private readonly string _alias10; private readonly string _alias11; private readonly string _alias12; private readonly string _alias13; private readonly string _alias14; internal DynamicTypedSelectScopeQueryBuilder(DynamicTypedSelectQueryBuilder builder, string alias1, string alias2, string alias3, string alias4, string alias5, string alias6, string alias7, string alias8, string alias9, string alias10, string alias11, string alias12, string alias13, string alias14) : base(builder) { _alias1 = alias1; _alias2 = alias2; _alias3 = alias3; _alias4 = alias4; _alias5 = alias5; _alias6 = alias6; _alias7 = alias7; _alias8 = alias8; _alias9 = alias9; _alias10 = alias10; _alias11 = alias11; _alias12 = alias12; _alias13 = alias13; _alias14 = alias14; } public IDynamicTypedSelectScopeQueryBuilder Join(Func, TypedScopeJoinBuilder> specification) { if (specification == null) throw new ArgumentNullException("specification"); TypedScopeJoinBuilder spec = specification(new TypedScopeJoinBuilder()); if (spec == null) throw new ArgumentException("Join specification cannot resolve to null.", "specification"); string alias = string.IsNullOrEmpty(spec.Alias) ? "t15" : spec.Alias; string condition = spec.OnSqlPredicate == null ? spec.OnRawCondition : Builder.RenderScopeSqlPredicate(spec.OnSqlPredicate(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9), new TypedTableContext(_alias10), new TypedTableContext(_alias11), new TypedTableContext(_alias12), new TypedTableContext(_alias13), new TypedTableContext(_alias14), new TypedTableContext(alias))); Builder.Join(j => ApplyJoinSpec(j, alias, spec.CustomJoinType, spec.JoinType, spec.UseNoLock, condition)); return new DynamicTypedSelectScopeQueryBuilder(Builder, _alias1, _alias2, _alias3, _alias4, _alias5, _alias6, _alias7, _alias8, _alias9, _alias10, _alias11, _alias12, _alias13, _alias14, alias); } public IDynamicTypedSelectScopeQueryBuilder SelectSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable>[] selectors) { if (selector == null) throw new ArgumentNullException("selector"); AddSelect(selector(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9), new TypedTableContext(_alias10), new TypedTableContext(_alias11), new TypedTableContext(_alias12), new TypedTableContext(_alias13), new TypedTableContext(_alias14))); if (selectors != null) foreach (var item in selectors) AddSelect(item(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9), new TypedTableContext(_alias10), new TypedTableContext(_alias11), new TypedTableContext(_alias12), new TypedTableContext(_alias13), new TypedTableContext(_alias14))); return this; } public IDynamicTypedSelectScopeQueryBuilder WhereSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate) { if (predicate == null) throw new ArgumentNullException("predicate"); AddWhere(predicate(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9), new TypedTableContext(_alias10), new TypedTableContext(_alias11), new TypedTableContext(_alias12), new TypedTableContext(_alias13), new TypedTableContext(_alias14))); return this; } public IDynamicTypedSelectScopeQueryBuilder HavingSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate) { if (predicate == null) throw new ArgumentNullException("predicate"); AddHaving(predicate(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9), new TypedTableContext(_alias10), new TypedTableContext(_alias11), new TypedTableContext(_alias12), new TypedTableContext(_alias13), new TypedTableContext(_alias14))); return this; } public IDynamicTypedSelectScopeQueryBuilder GroupBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression>[] selectors) { if (selector == null) throw new ArgumentNullException("selector"); AddGroupBy(selector(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9), new TypedTableContext(_alias10), new TypedTableContext(_alias11), new TypedTableContext(_alias12), new TypedTableContext(_alias13), new TypedTableContext(_alias14))); if (selectors != null) foreach (var item in selectors) AddGroupBy(item(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9), new TypedTableContext(_alias10), new TypedTableContext(_alias11), new TypedTableContext(_alias12), new TypedTableContext(_alias13), new TypedTableContext(_alias14))); return this; } public IDynamicTypedSelectScopeQueryBuilder OrderBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression>[] selectors) { if (selector == null) throw new ArgumentNullException("selector"); AddOrderBy(selector(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9), new TypedTableContext(_alias10), new TypedTableContext(_alias11), new TypedTableContext(_alias12), new TypedTableContext(_alias13), new TypedTableContext(_alias14))); if (selectors != null) foreach (var item in selectors) AddOrderBy(item(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9), new TypedTableContext(_alias10), new TypedTableContext(_alias11), new TypedTableContext(_alias12), new TypedTableContext(_alias13), new TypedTableContext(_alias14))); return this; } } internal sealed class DynamicTypedSelectScopeQueryBuilder : DynamicTypedSelectScopeQueryBuilderBase, IDynamicTypedSelectScopeQueryBuilder { private readonly string _alias1; private readonly string _alias2; private readonly string _alias3; private readonly string _alias4; private readonly string _alias5; private readonly string _alias6; private readonly string _alias7; private readonly string _alias8; private readonly string _alias9; private readonly string _alias10; private readonly string _alias11; private readonly string _alias12; private readonly string _alias13; private readonly string _alias14; private readonly string _alias15; internal DynamicTypedSelectScopeQueryBuilder(DynamicTypedSelectQueryBuilder builder, string alias1, string alias2, string alias3, string alias4, string alias5, string alias6, string alias7, string alias8, string alias9, string alias10, string alias11, string alias12, string alias13, string alias14, string alias15) : base(builder) { _alias1 = alias1; _alias2 = alias2; _alias3 = alias3; _alias4 = alias4; _alias5 = alias5; _alias6 = alias6; _alias7 = alias7; _alias8 = alias8; _alias9 = alias9; _alias10 = alias10; _alias11 = alias11; _alias12 = alias12; _alias13 = alias13; _alias14 = alias14; _alias15 = alias15; } public IDynamicTypedSelectScopeQueryBuilder Join(Func, TypedScopeJoinBuilder> specification) { if (specification == null) throw new ArgumentNullException("specification"); TypedScopeJoinBuilder spec = specification(new TypedScopeJoinBuilder()); if (spec == null) throw new ArgumentException("Join specification cannot resolve to null.", "specification"); string alias = string.IsNullOrEmpty(spec.Alias) ? "t16" : spec.Alias; string condition = spec.OnSqlPredicate == null ? spec.OnRawCondition : Builder.RenderScopeSqlPredicate(spec.OnSqlPredicate(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9), new TypedTableContext(_alias10), new TypedTableContext(_alias11), new TypedTableContext(_alias12), new TypedTableContext(_alias13), new TypedTableContext(_alias14), new TypedTableContext(_alias15), new TypedTableContext(alias))); Builder.Join(j => ApplyJoinSpec(j, alias, spec.CustomJoinType, spec.JoinType, spec.UseNoLock, condition)); return new DynamicTypedSelectScopeQueryBuilder(Builder, _alias1, _alias2, _alias3, _alias4, _alias5, _alias6, _alias7, _alias8, _alias9, _alias10, _alias11, _alias12, _alias13, _alias14, _alias15, alias); } public IDynamicTypedSelectScopeQueryBuilder SelectSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable>[] selectors) { if (selector == null) throw new ArgumentNullException("selector"); AddSelect(selector(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9), new TypedTableContext(_alias10), new TypedTableContext(_alias11), new TypedTableContext(_alias12), new TypedTableContext(_alias13), new TypedTableContext(_alias14), new TypedTableContext(_alias15))); if (selectors != null) foreach (var item in selectors) AddSelect(item(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9), new TypedTableContext(_alias10), new TypedTableContext(_alias11), new TypedTableContext(_alias12), new TypedTableContext(_alias13), new TypedTableContext(_alias14), new TypedTableContext(_alias15))); return this; } public IDynamicTypedSelectScopeQueryBuilder WhereSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate) { if (predicate == null) throw new ArgumentNullException("predicate"); AddWhere(predicate(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9), new TypedTableContext(_alias10), new TypedTableContext(_alias11), new TypedTableContext(_alias12), new TypedTableContext(_alias13), new TypedTableContext(_alias14), new TypedTableContext(_alias15))); return this; } public IDynamicTypedSelectScopeQueryBuilder HavingSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate) { if (predicate == null) throw new ArgumentNullException("predicate"); AddHaving(predicate(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9), new TypedTableContext(_alias10), new TypedTableContext(_alias11), new TypedTableContext(_alias12), new TypedTableContext(_alias13), new TypedTableContext(_alias14), new TypedTableContext(_alias15))); return this; } public IDynamicTypedSelectScopeQueryBuilder GroupBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression>[] selectors) { if (selector == null) throw new ArgumentNullException("selector"); AddGroupBy(selector(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9), new TypedTableContext(_alias10), new TypedTableContext(_alias11), new TypedTableContext(_alias12), new TypedTableContext(_alias13), new TypedTableContext(_alias14), new TypedTableContext(_alias15))); if (selectors != null) foreach (var item in selectors) AddGroupBy(item(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9), new TypedTableContext(_alias10), new TypedTableContext(_alias11), new TypedTableContext(_alias12), new TypedTableContext(_alias13), new TypedTableContext(_alias14), new TypedTableContext(_alias15))); return this; } public IDynamicTypedSelectScopeQueryBuilder OrderBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression>[] selectors) { if (selector == null) throw new ArgumentNullException("selector"); AddOrderBy(selector(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9), new TypedTableContext(_alias10), new TypedTableContext(_alias11), new TypedTableContext(_alias12), new TypedTableContext(_alias13), new TypedTableContext(_alias14), new TypedTableContext(_alias15))); if (selectors != null) foreach (var item in selectors) AddOrderBy(item(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9), new TypedTableContext(_alias10), new TypedTableContext(_alias11), new TypedTableContext(_alias12), new TypedTableContext(_alias13), new TypedTableContext(_alias14), new TypedTableContext(_alias15))); return this; } } internal sealed class DynamicTypedSelectScopeQueryBuilder : DynamicTypedSelectScopeQueryBuilderBase, IDynamicTypedSelectScopeQueryBuilder { private readonly string _alias1; private readonly string _alias2; private readonly string _alias3; private readonly string _alias4; private readonly string _alias5; private readonly string _alias6; private readonly string _alias7; private readonly string _alias8; private readonly string _alias9; private readonly string _alias10; private readonly string _alias11; private readonly string _alias12; private readonly string _alias13; private readonly string _alias14; private readonly string _alias15; private readonly string _alias16; internal DynamicTypedSelectScopeQueryBuilder(DynamicTypedSelectQueryBuilder builder, string alias1, string alias2, string alias3, string alias4, string alias5, string alias6, string alias7, string alias8, string alias9, string alias10, string alias11, string alias12, string alias13, string alias14, string alias15, string alias16) : base(builder) { _alias1 = alias1; _alias2 = alias2; _alias3 = alias3; _alias4 = alias4; _alias5 = alias5; _alias6 = alias6; _alias7 = alias7; _alias8 = alias8; _alias9 = alias9; _alias10 = alias10; _alias11 = alias11; _alias12 = alias12; _alias13 = alias13; _alias14 = alias14; _alias15 = alias15; _alias16 = alias16; } public IDynamicTypedSelectScopeQueryBuilder SelectSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlSelectable>[] selectors) { if (selector == null) throw new ArgumentNullException("selector"); AddSelect(selector(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9), new TypedTableContext(_alias10), new TypedTableContext(_alias11), new TypedTableContext(_alias12), new TypedTableContext(_alias13), new TypedTableContext(_alias14), new TypedTableContext(_alias15), new TypedTableContext(_alias16))); if (selectors != null) foreach (var item in selectors) AddSelect(item(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9), new TypedTableContext(_alias10), new TypedTableContext(_alias11), new TypedTableContext(_alias12), new TypedTableContext(_alias13), new TypedTableContext(_alias14), new TypedTableContext(_alias15), new TypedTableContext(_alias16))); return this; } public IDynamicTypedSelectScopeQueryBuilder WhereSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate) { if (predicate == null) throw new ArgumentNullException("predicate"); AddWhere(predicate(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9), new TypedTableContext(_alias10), new TypedTableContext(_alias11), new TypedTableContext(_alias12), new TypedTableContext(_alias13), new TypedTableContext(_alias14), new TypedTableContext(_alias15), new TypedTableContext(_alias16))); return this; } public IDynamicTypedSelectScopeQueryBuilder HavingSql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlPredicate> predicate) { if (predicate == null) throw new ArgumentNullException("predicate"); AddHaving(predicate(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9), new TypedTableContext(_alias10), new TypedTableContext(_alias11), new TypedTableContext(_alias12), new TypedTableContext(_alias13), new TypedTableContext(_alias14), new TypedTableContext(_alias15), new TypedTableContext(_alias16))); return this; } public IDynamicTypedSelectScopeQueryBuilder GroupBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlExpression>[] selectors) { if (selector == null) throw new ArgumentNullException("selector"); AddGroupBy(selector(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9), new TypedTableContext(_alias10), new TypedTableContext(_alias11), new TypedTableContext(_alias12), new TypedTableContext(_alias13), new TypedTableContext(_alias14), new TypedTableContext(_alias15), new TypedTableContext(_alias16))); if (selectors != null) foreach (var item in selectors) AddGroupBy(item(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9), new TypedTableContext(_alias10), new TypedTableContext(_alias11), new TypedTableContext(_alias12), new TypedTableContext(_alias13), new TypedTableContext(_alias14), new TypedTableContext(_alias15), new TypedTableContext(_alias16))); return this; } public IDynamicTypedSelectScopeQueryBuilder OrderBySql(Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression> selector, params Func, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedTableContext, TypedSqlOrderExpression>[] selectors) { if (selector == null) throw new ArgumentNullException("selector"); AddOrderBy(selector(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9), new TypedTableContext(_alias10), new TypedTableContext(_alias11), new TypedTableContext(_alias12), new TypedTableContext(_alias13), new TypedTableContext(_alias14), new TypedTableContext(_alias15), new TypedTableContext(_alias16))); if (selectors != null) foreach (var item in selectors) AddOrderBy(item(new TypedTableContext(_alias1), new TypedTableContext(_alias2), new TypedTableContext(_alias3), new TypedTableContext(_alias4), new TypedTableContext(_alias5), new TypedTableContext(_alias6), new TypedTableContext(_alias7), new TypedTableContext(_alias8), new TypedTableContext(_alias9), new TypedTableContext(_alias10), new TypedTableContext(_alias11), new TypedTableContext(_alias12), new TypedTableContext(_alias13), new TypedTableContext(_alias14), new TypedTableContext(_alias15), new TypedTableContext(_alias16))); return this; } } /// Typed wrapper over with property-to-column translation. /// Mapped entity type. internal class DynamicTypedUpdateQueryBuilder : DynamicUpdateQueryBuilder, IDynamicTypedUpdateQueryBuilder { internal DynamicTypedUpdateQueryBuilder(DynamicDatabase db) : this(db, false) { } internal DynamicTypedUpdateQueryBuilder(DynamicDatabase db, bool mapType) : base(db) { if (mapType) this.Table(typeof(T)); else this.Table(typeof(T).Name); } public IDynamicTypedUpdateQueryBuilder Where(Expression> predicate) { TypedModifyHelper.ApplyWhere((c, o, v) => base.Where(c, o, v), predicate); return this; } public IDynamicTypedUpdateQueryBuilder Set(Expression> selector, object value) { base.Values(TypedModifyHelper.GetMappedColumn(selector), value); return this; } public IDynamicTypedUpdateQueryBuilder Values(T value) { base.Values(value); return this; } public IDynamicTypedUpdateQueryBuilder WhereSql(Func, TypedSqlPredicate> predicate) { string condition = TypedModifyHelper.RenderPredicate(predicate, null, RenderValue, Database.DecorateName, RenderSubQuery); if (string.IsNullOrEmpty(WhereCondition)) WhereCondition = condition; else WhereCondition = string.Format("{0} AND {1}", WhereCondition, condition); return this; } public IDynamicTypedUpdateQueryBuilder SetSql(Expression> selector, Func, TypedSqlExpression> valueFactory) { string column = FixObjectName(TypedModifyHelper.GetMappedColumn(selector), onlyColumn: true); string value = TypedModifyHelper.RenderExpression(valueFactory, null, RenderValue, Database.DecorateName, RenderSubQuery); string assignment = string.Format("{0} = {1}", column, value); _columns = _columns == null ? assignment : string.Format("{0}, {1}", _columns, assignment); return this; } public IDynamicTypedUpdateQueryBuilder SetSql(Func, object> values) { if (values == null) throw new ArgumentNullException("values"); object data = values(new TypedTableContext(null)); foreach (KeyValuePair item in data.ToDictionary()) { string column = FixObjectName(TypedModifyHelper.GetMappedColumnByName(typeof(T), item.Key), onlyColumn: true); string value = (item.Value as TypedSqlExpression) != null ? TypedModifyHelper.RenderExpression(u => (TypedSqlExpression)item.Value, null, RenderValue, Database.DecorateName, RenderSubQuery) : RenderValue(item.Value); string assignment = string.Format("{0} = {1}", column, value); _columns = _columns == null ? assignment : string.Format("{0}, {1}", _columns, assignment); } return this; } public new IDynamicTypedUpdateQueryBuilder Update(string column, object value) { base.Update(column, value); return this; } public new IDynamicTypedUpdateQueryBuilder Update(object conditions) { base.Update(conditions); return this; } public new IDynamicTypedUpdateQueryBuilder Set(params Func[] func) { base.Set(func); return this; } public new IDynamicTypedUpdateQueryBuilder Values(string column, object value) { base.Values(column, value); return this; } public new IDynamicTypedUpdateQueryBuilder Values(object o) { base.Values(o); return this; } public new IDynamicTypedUpdateQueryBuilder Where(Func func) { base.Where(func); return this; } public new IDynamicTypedUpdateQueryBuilder Where(DynamicColumn column) { base.Where(column); return this; } public new IDynamicTypedUpdateQueryBuilder Where(string column, DynamicColumn.CompareOperator op, object value) { base.Where(column, op, value); return this; } public new IDynamicTypedUpdateQueryBuilder Where(string column, object value) { base.Where(column, value); return this; } public new IDynamicTypedUpdateQueryBuilder Where(object conditions, bool schema = false) { base.Where(conditions, schema); return this; } private string RenderValue(object value) { if (value == null) return "NULL"; DynamicSchemaColumn? columnSchema = null; return ParseConstant(value, Parameters, columnSchema); } private string RenderSubQuery(Builders.IDynamicSelectQueryBuilder query) { foreach (KeyValuePair item in query.Parameters) if (!Parameters.ContainsKey(item.Key)) Parameters.Add(item.Key, item.Value); return query.CommandText(); } } /// Update query builder. internal class DynamicUpdateQueryBuilder : DynamicModifyBuilder, IDynamicUpdateQueryBuilder, DynamicQueryBuilder.IQueryWithWhere { protected string _columns; internal DynamicUpdateQueryBuilder(DynamicDatabase db) : base(db) { } public DynamicUpdateQueryBuilder(DynamicDatabase db, string tableName) : base(db, tableName) { } /// Generates the text this command will execute against the underlying database. /// The text to execute against the underlying database. /// This method must be override by derived classes. public override string CommandText() { ITableInfo info = Tables.Single(); return string.Format("UPDATE {0}{1} SET {2}{3}{4}", string.IsNullOrEmpty(info.Owner) ? string.Empty : string.Format("{0}.", Database.DecorateName(info.Owner)), Database.DecorateName(info.Name), _columns, string.IsNullOrEmpty(WhereCondition) ? string.Empty : " WHERE ", WhereCondition); } #region Update /// Add update value or where condition using schema. /// Update or where column name. /// Column value. /// Builder instance. public virtual IDynamicUpdateQueryBuilder Update(string column, object value) { DynamicSchemaColumn? col = GetColumnFromSchema(column); if (!col.HasValue && SupportSchema) throw new InvalidOperationException(string.Format("Column '{0}' not found in schema, can't use universal approach.", column)); if (col.HasValue && col.Value.IsKey) Where(column, value); else Values(column, value); return this; } /// Add update values and where condition columns using schema. /// Set values or conditions as properties and values of an object. /// Builder instance. public virtual IDynamicUpdateQueryBuilder Update(object conditions) { if (conditions is DynamicColumn) { DynamicColumn column = (DynamicColumn)conditions; DynamicSchemaColumn? col = column.Schema ?? GetColumnFromSchema(column.ColumnName); if (!col.HasValue && SupportSchema) throw new InvalidOperationException(string.Format("Column '{0}' not found in schema, can't use universal approach.", column)); if (col.HasValue && col.Value.IsKey) Where(column); else Values(column.ColumnName, column.Value); return this; } IDictionary dict = conditions.ToDictionary(); DynamicTypeMap mapper = DynamicMapperCache.GetMapper(conditions.GetType()); foreach (KeyValuePair con in dict) { if (mapper.Ignored.Contains(con.Key)) continue; string colName = mapper != null ? mapper.PropertyMap.TryGetValue(con.Key) ?? con.Key : con.Key; DynamicSchemaColumn? col = GetColumnFromSchema(colName); if (!col.HasValue && SupportSchema) throw new InvalidOperationException(string.Format("Column '{0}' not found in schema, can't use universal approach.", colName)); if (col.HasValue) { colName = col.Value.Name; if (col.Value.IsKey) { Where(colName, con.Value); continue; } } DynamicPropertyInvoker propMap = mapper.ColumnsMap.TryGetValue(colName.ToLower()); if (propMap == null || propMap.Column == null || !propMap.Column.IsNoUpdate) Values(colName, con.Value); } return this; } #endregion Update #region Values /// /// Specifies the columns to update using the dynamic lambda expressions given. Each expression correspond to one /// column, and can: /// - Resolve to a string, in this case a '=' must appear in the string. /// - Resolve to a expression with the form: 'x => x.Column = Value'. /// /// The specifications. /// This instance to permit chaining. public virtual IDynamicUpdateQueryBuilder Set(params Func[] func) { if (func == null) throw new ArgumentNullException("Array of specifications cannot be null."); int index = -1; foreach (Func f in func) { index++; if (f == null) throw new ArgumentNullException(string.Format("Specification #{0} cannot be null.", index)); object result = null; using (DynamicParser p = DynamicParser.Parse(f)) { result = p.Result; if (result == null) throw new ArgumentException(string.Format("Specification #{0} resolves to null.", index)); string main = null; string value = null; string str = null; // When 'x => x.Table.Column = value' or 'x => x.Column = value'... if (result is DynamicParser.Node.SetMember) { DynamicParser.Node.SetMember node = (DynamicParser.Node.SetMember)result; DynamicSchemaColumn? col = GetColumnFromSchema(node.Name); main = Database.DecorateName(node.Name); value = Parse(node.Value, ref col, pars: Parameters, nulls: true); str = string.Format("{0} = {1}", main, value); _columns = _columns == null ? str : string.Format("{0}, {1}", _columns, str); continue; } else if (!(result is DynamicParser.Node) && !result.GetType().IsValueType) { Values(result); continue; } // Other specifications are considered invalid... string err = string.Format("Specification '{0}' is invalid.", result); str = Parse(result); if (str.Contains("=")) err += " May have you used a '==' instead of a '=' operator?"; throw new ArgumentException(err); } } return this; } /// Add insert fields. /// Insert column. /// Insert value. /// Builder instance. public virtual IDynamicUpdateQueryBuilder Values(string column, object value) { if (value is DynamicColumn) { DynamicColumn v = (DynamicColumn)value; if (string.IsNullOrEmpty(v.ColumnName)) v.ColumnName = column; return Values(v); } return Values(new DynamicColumn { ColumnName = column, Value = value, }); } /// Add insert fields. /// Set insert value as properties and values of an object. /// Builder instance. public virtual IDynamicUpdateQueryBuilder Values(object o) { if (o is DynamicColumn) { DynamicColumn column = (DynamicColumn)o; DynamicSchemaColumn? col = column.Schema ?? GetColumnFromSchema(column.ColumnName); string main = FixObjectName(column.ColumnName, onlyColumn: true); string value = Parse(column.Value, ref col, pars: Parameters, nulls: true); string str = string.Format("{0} = {1}", main, value); _columns = _columns == null ? str : string.Format("{0}, {1}", _columns, str); return this; } IDictionary dict = o.ToDictionary(); DynamicTypeMap mapper = DynamicMapperCache.GetMapper(o.GetType()); if (mapper != null) { foreach (KeyValuePair con in dict) if (!mapper.Ignored.Contains(con.Key)) Values(mapper.PropertyMap.TryGetValue(con.Key) ?? con.Key, con.Value); } else foreach (KeyValuePair con in dict) Values(con.Key, con.Value); return this; } #endregion Values #region Where /// /// Adds to the 'Where' clause the contents obtained from parsing the dynamic lambda expression given. The condition /// is parsed to the appropriate syntax, where the specific customs virtual methods supported by the parser are used /// as needed. /// - If several Where() methods are chained their contents are, by default, concatenated with an 'AND' operator. /// - The 'And()' and 'Or()' virtual method can be used to concatenate with an 'OR' or an 'AND' operator, as in: /// 'Where( x => x.Or( condition ) )'. /// /// The specification. /// This instance to permit chaining. public virtual IDynamicUpdateQueryBuilder Where(Func func) { return this.InternalWhere(func); } /// Add where condition. /// Condition column with operator and value. /// Builder instance. public virtual IDynamicUpdateQueryBuilder Where(DynamicColumn column) { return this.InternalWhere(column); } /// Add where condition. /// Condition column. /// Condition operator. /// Condition value. /// Builder instance. public virtual IDynamicUpdateQueryBuilder Where(string column, DynamicColumn.CompareOperator op, object value) { return this.InternalWhere(column, op, value); } /// Add where condition. /// Condition column. /// Condition value. /// Builder instance. public virtual IDynamicUpdateQueryBuilder Where(string column, object value) { return this.InternalWhere(column, value); } /// Add where condition. /// Set conditions as properties and values of an object. /// If true use schema to determine key columns and ignore those which /// aren't keys. /// Builder instance. public virtual IDynamicUpdateQueryBuilder Where(object conditions, bool schema = false) { return this.InternalWhere(conditions, schema); } #endregion Where #region IExtendedDisposable /// Performs application-defined tasks associated with /// freeing, releasing, or resetting unmanaged resources. public override void Dispose() { base.Dispose(); _columns = null; } #endregion IExtendedDisposable } /// Helper methods for typed modify builders. internal static class TypedModifyHelper { private sealed class ModifyRenderContext : ITypedSqlRenderContext { private readonly Func, string> _resolveColumn; private readonly Func _renderValue; private readonly Func _decorateName; private readonly Func _renderSubQuery; public ModifyRenderContext(Func, string> resolveColumn, Func renderValue, Func decorateName, Func renderSubQuery) { _resolveColumn = resolveColumn; _renderValue = renderValue; _decorateName = decorateName; _renderSubQuery = renderSubQuery; } public string ResolveColumn(Type modelType, string memberName, string alias) { return _resolveColumn(modelType, memberName, alias, _decorateName); } public string RenderValue(object value) { return _renderValue(value); } public string DecorateName(string name) { return _decorateName(name); } public string RenderSubQuery(IDynamicSelectQueryBuilder query) { return _renderSubQuery(query); } } public static string GetMappedColumn(Expression> selector) { if (selector == null) throw new ArgumentNullException("selector"); return GetMappedColumn(typeof(T), selector.Body); } public static void ApplyWhere(Action addCondition, Expression> predicate) { if (addCondition == null) throw new ArgumentNullException("addCondition"); if (predicate == null) throw new ArgumentNullException("predicate"); ApplyWhereInternal(typeof(T), addCondition, predicate.Body); } public static string RenderPredicate( Func, TypedSqlPredicate> predicate, string alias, Func renderValue, Func decorateName, Func renderSubQuery) { if (predicate == null) throw new ArgumentNullException("predicate"); ModifyRenderContext context = new ModifyRenderContext(ResolveColumn, renderValue, decorateName, renderSubQuery); return predicate(new TypedTableContext(alias)).Render(context); } public static string RenderExpression( Func, TypedSqlExpression> expression, string alias, Func renderValue, Func decorateName, Func renderSubQuery) { if (expression == null) throw new ArgumentNullException("expression"); ModifyRenderContext context = new ModifyRenderContext(ResolveColumn, renderValue, decorateName, renderSubQuery); return expression(new TypedTableContext(alias)).Render(context); } private static void ApplyWhereInternal(Type modelType, Action addCondition, Expression expression) { expression = UnwrapConvert(expression); BinaryExpression be = expression as BinaryExpression; if (be == null) throw new NotSupportedException(string.Format("Typed where expression is currently limited to AND-composed binary comparisons: {0}", expression)); if (be.NodeType == ExpressionType.AndAlso || be.NodeType == ExpressionType.And) { ApplyWhereInternal(modelType, addCondition, be.Left); ApplyWhereInternal(modelType, addCondition, be.Right); return; } string col = GetMappedColumn(modelType, be.Left); object val = EvaluateExpression(be.Right); switch (be.NodeType) { case ExpressionType.Equal: addCondition(col, DynamicColumn.CompareOperator.Eq, val); return; case ExpressionType.NotEqual: addCondition(col, DynamicColumn.CompareOperator.Not, val); return; case ExpressionType.GreaterThan: addCondition(col, DynamicColumn.CompareOperator.Gt, val); return; case ExpressionType.GreaterThanOrEqual: addCondition(col, DynamicColumn.CompareOperator.Gte, val); return; case ExpressionType.LessThan: addCondition(col, DynamicColumn.CompareOperator.Lt, val); return; case ExpressionType.LessThanOrEqual: addCondition(col, DynamicColumn.CompareOperator.Lte, val); return; } throw new NotSupportedException(string.Format("Typed where expression is currently limited to AND-composed binary comparisons: {0}", expression)); } private static string GetMappedColumn(Type modelType, Expression expression) { expression = UnwrapConvert(expression); MemberExpression member = expression as MemberExpression; if (member == null) throw new NotSupportedException(string.Format("Selector must target a mapped property: {0}", expression)); DynamicTypeMap mapper = DynamicMapperCache.GetMapper(modelType); if (mapper == null) throw new InvalidOperationException(string.Format("Cant assign unmapable type as a table ({0}).", modelType.FullName)); return mapper.PropertyMap.TryGetValue(member.Member.Name) ?? mapper.PropertyMap .Where(x => string.Equals(x.Key, member.Member.Name, StringComparison.OrdinalIgnoreCase)) .Select(x => x.Value) .FirstOrDefault() ?? member.Member.Name; } private static string ResolveColumn(Type modelType, string memberName, string alias, Func decorateName) { string mapped = GetMappedColumnByName(modelType, memberName); return string.IsNullOrEmpty(alias) ? decorateName(mapped) : string.Format("{0}.{1}", alias, decorateName(mapped)); } internal static string GetMappedColumnByName(Type modelType, string memberName) { DynamicTypeMap mapper = DynamicMapperCache.GetMapper(modelType); if (mapper == null) throw new InvalidOperationException(string.Format("Cant assign unmapable type as a table ({0}).", modelType.FullName)); return mapper.PropertyMap.TryGetValue(memberName) ?? mapper.PropertyMap.Where(x => string.Equals(x.Key, memberName, StringComparison.OrdinalIgnoreCase)).Select(x => x.Value).FirstOrDefault() ?? memberName; } private static Expression UnwrapConvert(Expression expression) { while (expression is UnaryExpression && (((UnaryExpression)expression).NodeType == ExpressionType.Convert || ((UnaryExpression)expression).NodeType == ExpressionType.ConvertChecked)) expression = ((UnaryExpression)expression).Operand; return expression; } private static object EvaluateExpression(Expression expression) { expression = UnwrapConvert(expression); var objectMember = Expression.Convert(expression, typeof(object)); var getter = Expression.Lambda>(objectMember).Compile(); return getter(); } } } } namespace Helpers { /// Defines methods to support the comparison of collections for equality. /// The type of collection to compare. public class CollectionComparer : IEqualityComparer> { /// Determines whether the specified objects are equal. /// The first object of type T to compare. /// The second object of type T to compare. /// Returns true if the specified objects are equal; otherwise, false. bool IEqualityComparer>.Equals(IEnumerable first, IEnumerable second) { return Equals(first, second); } /// Returns a hash code for the specified object. /// The enumerable for which a hash code is to be returned. /// A hash code for the specified object. int IEqualityComparer>.GetHashCode(IEnumerable enumerable) { return GetHashCode(enumerable); } /// Returns a hash code for the specified object. /// The enumerable for which a hash code is to be returned. /// A hash code for the specified object. public static int GetHashCode(IEnumerable enumerable) { int hash = 17; foreach (T val in enumerable.OrderBy(x => x)) hash = (hash * 23) + val.GetHashCode(); return hash; } /// Determines whether the specified objects are equal. /// The first object of type T to compare. /// The second object of type T to compare. /// Returns true if the specified objects are equal; otherwise, false. public static bool Equals(IEnumerable first, IEnumerable second) { if ((first == null) != (second == null)) return false; if (!object.ReferenceEquals(first, second) && (first != null)) { if (first.Count() != second.Count()) return false; if ((first.Count() != 0) && HaveMismatchedElement(first, second)) return false; } return true; } private static bool HaveMismatchedElement(IEnumerable first, IEnumerable second) { int firstCount; int secondCount; Dictionary firstElementCounts = GetElementCounts(first, out firstCount); Dictionary secondElementCounts = GetElementCounts(second, out secondCount); if (firstCount != secondCount) return true; foreach (KeyValuePair kvp in firstElementCounts) if (kvp.Value != (secondElementCounts.TryGetNullable(kvp.Key) ?? 0)) return true; return false; } private static Dictionary GetElementCounts(IEnumerable enumerable, out int nullCount) { Dictionary dictionary = new Dictionary(); nullCount = 0; foreach (T element in enumerable) { if (element == null) nullCount++; else { int count = dictionary.TryGetNullable(element) ?? 0; dictionary[element] = ++count; } } return dictionary; } } /// Extensions for data reader handling. public static class DataReaderExtensions { /// Gets the data table from data reader. /// The data reader. /// The name to give the table. If tableName is null or an empty string, a default name is given when added to the System.Data.DataTableCollection. /// The namespace for the XML representation of the data stored in the DataTable. /// public static DataTable ToDataTable(this IDataReader r, string name = null, string nameSpace = null) { DataTable schemaTable = r.GetSchemaTable(); DataTable resultTable = new DataTable(name, nameSpace); foreach (DataRow col in schemaTable.Rows) { dynamic c = col.RowToDynamicUpper(); DataColumn dataColumn = new DataColumn(); dataColumn.ColumnName = c.COLUMNNAME; dataColumn.DataType = (Type)c.DATATYPE; dataColumn.ReadOnly = true; dataColumn.Unique = c.ISUNIQUE; resultTable.Columns.Add(dataColumn); } while (r.Read()) { DataRow row = resultTable.NewRow(); for (int i = 0; i < resultTable.Columns.Count; i++) row[i] = r[i]; resultTable.Rows.Add(row); } return resultTable; } } internal static class DynamicProcedureParameterBinder { internal sealed class BindingResult { public bool ReturnValueAdded { get; set; } public Dictionary ReturnParameters { get; set; } } internal static bool CanBind(object item) { if (!DynamicProcedureResultBinder.IsProcedureContract(item)) return false; return GetBindableProperties(item.GetType()).Any(); } internal static BindingResult Bind(DynamicDatabase db, IDbCommand cmd, object item) { if (db == null) throw new ArgumentNullException("db"); if (cmd == null) throw new ArgumentNullException("cmd"); if (item == null) throw new ArgumentNullException("item"); BindingResult result = new BindingResult(); foreach (PropertyInfo property in GetBindableProperties(item.GetType())) { ProcedureParameterAttribute procAttr = property.GetCustomAttributes(typeof(ProcedureParameterAttribute), true).Cast().FirstOrDefault(); ColumnAttribute colAttr = property.GetCustomAttributes(typeof(ColumnAttribute), true).Cast().FirstOrDefault(); string name = (procAttr != null && !string.IsNullOrEmpty(procAttr.Name) ? procAttr.Name : null) ?? (colAttr != null && !string.IsNullOrEmpty(colAttr.Name) ? colAttr.Name : null) ?? property.Name; ParameterDirection direction = procAttr == null ? ParameterDirection.Input : procAttr.Direction; object value = property.GetValue(item, null); DbType dbType = ResolveDbType(property.PropertyType, value, procAttr, colAttr, direction); int size = ResolveSize(dbType, value, procAttr, colAttr, direction); byte precision = (procAttr != null && procAttr.Precision != ProcedureParameterAttribute.UnspecifiedByte ? procAttr.Precision : default(byte)); byte scale = (procAttr != null && procAttr.Scale != ProcedureParameterAttribute.UnspecifiedByte ? procAttr.Scale : default(byte)); if (procAttr == null && colAttr != null) { precision = colAttr.Precision ?? precision; scale = colAttr.Scale ?? scale; } if (direction == ParameterDirection.ReturnValue) result.ReturnValueAdded = true; if (direction == ParameterDirection.Output || direction == ParameterDirection.InputOutput || direction == ParameterDirection.ReturnValue) { if (result.ReturnParameters == null) result.ReturnParameters = new Dictionary(); result.ReturnParameters.Add(name, cmd.Parameters.Count); } cmd.AddParameter( db.GetParameterName(name), direction, dbType, size, precision, scale, direction == ParameterDirection.Output || direction == ParameterDirection.ReturnValue ? DBNull.Value : (value ?? DBNull.Value)); } return result; } private static IEnumerable GetBindableProperties(Type type) { return type.GetProperties(BindingFlags.Instance | BindingFlags.Public) .Where(x => x.CanRead && x.GetIndexParameters().Length == 0) .Where(x => x.GetCustomAttributes(typeof(ProcedureParameterAttribute), true).Any() || x.GetCustomAttributes(typeof(ColumnAttribute), true).Any()) .OrderBy(x => { ProcedureParameterAttribute attr = x.GetCustomAttributes(typeof(ProcedureParameterAttribute), true).Cast().FirstOrDefault(); return attr == null ? int.MaxValue : attr.Order; }) .ThenBy(x => x.MetadataToken); } private static DbType ResolveDbType(Type propertyType, object value, ProcedureParameterAttribute procAttr, ColumnAttribute colAttr, ParameterDirection direction) { if (procAttr != null && (int)procAttr.DbType != ProcedureParameterAttribute.UnspecifiedDbType) return procAttr.DbType; if (colAttr != null && colAttr.Type.HasValue) return colAttr.Type.Value; Type targetType = Nullable.GetUnderlyingType(propertyType) ?? propertyType; if (targetType == typeof(object) && value != null) targetType = value.GetType(); if (value == null && direction == ParameterDirection.ReturnValue) return DbType.Int32; return targetType.ToDbType(); } private static int ResolveSize(DbType dbType, object value, ProcedureParameterAttribute procAttr, ColumnAttribute colAttr, ParameterDirection direction) { if (procAttr != null && procAttr.Size != ProcedureParameterAttribute.UnspecifiedSize) return procAttr.Size; if (colAttr != null && colAttr.Size.HasValue) return colAttr.Size.Value; if (direction == ParameterDirection.ReturnValue) return 4; if (dbType == DbType.AnsiString || dbType == DbType.AnsiStringFixedLength) { if (value != null) return value.ToString().Length > 8000 ? -1 : 8000; return 8000; } if (dbType == DbType.String || dbType == DbType.StringFixedLength) { if (value != null) return value.ToString().Length > 4000 ? -1 : 4000; return 4000; } return 0; } } internal static class DynamicProcedureResultBinder { private sealed class ResultMemberBinding { public ProcedureResultAttribute Attribute { get; set; } public MemberInfo Member { get; set; } public Type MemberType { get; set; } public int SortOrder { get; set; } public void SetValue(object instance, object value) { PropertyInfo property = Member as PropertyInfo; if (property != null) { property.SetValue(instance, value, null); return; } FieldInfo field = Member as FieldInfo; if (field != null) field.SetValue(instance, value); } } internal static bool IsProcedureContract(object item) { return item is IProcedureParameters; } internal static Type GetDeclaredResultType(object item) { if (item == null) return null; Type iface = item.GetType().GetInterfaces() .FirstOrDefault(x => x.IsGenericType && x.GetGenericTypeDefinition() == typeof(IProcedureParameters<>)); return iface == null ? null : iface.GetGenericArguments()[0]; } internal static bool CanReadResults(Type resultType) { return resultType != null && (typeof(IProcedureResultReader).IsAssignableFrom(resultType) || GetResultMemberBindings(resultType).Count > 0); } internal static bool HasDeclaredResultBinding(Type resultType) { return CanReadResults(resultType); } internal static object CreateDeclaredResult(Type resultType) { if (resultType == null) return null; DynamicTypeMap mapper = DynamicMapperCache.GetMapper(resultType); if (mapper != null) return mapper.Creator(); return Activator.CreateInstance(resultType); } internal static object ReadDeclaredResult(Type resultType, IDataReader reader) { if (!CanReadResults(resultType)) throw new InvalidOperationException(string.Format("Type '{0}' does not declare a supported procedure result binding.", resultType == null ? "" : resultType.FullName)); object instance = CreateDeclaredResult(resultType); IList bindings = GetResultMemberBindings(resultType); if (bindings.Count > 0) BindResultMembers(instance, reader, bindings); else ((IProcedureResultReader)instance).ReadResults(reader); return instance; } internal static object BindPayload(Type resultType, string mainResultName, object mainResult, IDictionary returnValues, object existing = null) { if (resultType == null) return existing ?? returnValues.ToDynamic(); Dictionary payload = new Dictionary(); if (mainResultName != null) payload[mainResultName] = mainResult == DBNull.Value ? null : mainResult; if (returnValues != null) foreach (KeyValuePair item in returnValues) payload[item.Key] = item.Value == DBNull.Value ? null : item.Value; DynamicTypeMap mapper = DynamicMapperCache.GetMapper(resultType); object instance = existing; if (mapper != null) instance = mapper.Map(payload.ToDynamic(), existing ?? mapper.Creator()); else if (instance == null) instance = payload.ToDynamic(); if (instance != null && resultType.IsInstanceOfType(instance)) BindMainResultMembers(instance, mainResult); return instance; } private static IList GetResultMemberBindings(Type resultType) { var properties = resultType.GetProperties(BindingFlags.Instance | BindingFlags.Public) .Where(x => x.CanWrite && x.GetIndexParameters().Length == 0) .Select(x => new ResultMemberBinding { Member = x, MemberType = x.PropertyType, SortOrder = x.MetadataToken, Attribute = x.GetCustomAttributes(typeof(ProcedureResultAttribute), true).Cast().FirstOrDefault() }); var fields = resultType.GetFields(BindingFlags.Instance | BindingFlags.Public) .Where(x => !x.IsInitOnly && !x.IsLiteral) .Select(x => new ResultMemberBinding { Member = x, MemberType = x.FieldType, SortOrder = x.MetadataToken, Attribute = x.GetCustomAttributes(typeof(ProcedureResultAttribute), true).Cast().FirstOrDefault() }); return properties.Concat(fields) .Where(x => x.Attribute != null) .Where(x => !IsMainResultBinding(x.Attribute)) .OrderBy(x => x.Attribute.ResultIndex) .ThenBy(x => x.SortOrder) .ToList(); } private static IList GetMainResultBindings(Type resultType) { var properties = resultType.GetProperties(BindingFlags.Instance | BindingFlags.Public) .Where(x => x.CanWrite && x.GetIndexParameters().Length == 0) .Select(x => new ResultMemberBinding { Member = x, MemberType = x.PropertyType, SortOrder = x.MetadataToken, Attribute = x.GetCustomAttributes(typeof(ProcedureResultAttribute), true).Cast().FirstOrDefault() }); var fields = resultType.GetFields(BindingFlags.Instance | BindingFlags.Public) .Where(x => !x.IsInitOnly && !x.IsLiteral) .Select(x => new ResultMemberBinding { Member = x, MemberType = x.FieldType, SortOrder = x.MetadataToken, Attribute = x.GetCustomAttributes(typeof(ProcedureResultAttribute), true).Cast().FirstOrDefault() }); return properties.Concat(fields) .Where(x => x.Attribute != null) .Where(x => IsMainResultBinding(x.Attribute)) .OrderBy(x => x.SortOrder) .ToList(); } private static void BindResultMembers(object instance, IDataReader reader, IList bindings) { ValidateBindings(instance.GetType(), bindings); int currentIndex = 0; bool hasCurrent = true; for (int i = 0; i < bindings.Count; i++) { ResultMemberBinding binding = bindings[i]; while (hasCurrent && currentIndex < binding.Attribute.ResultIndex) { hasCurrent = reader.NextResult(); currentIndex++; } if (!hasCurrent || currentIndex != binding.Attribute.ResultIndex) break; object value = ReadResultValue(binding.MemberType, binding.Attribute, reader); binding.SetValue(instance, value); if (i + 1 < bindings.Count) { hasCurrent = reader.NextResult(); currentIndex++; } } } private static void ValidateBindings(Type resultType, IList bindings) { var duplicates = bindings.GroupBy(x => x.Attribute.ResultIndex).FirstOrDefault(x => x.Count() > 1); if (duplicates != null) throw new InvalidOperationException(string.Format("Type '{0}' defines multiple ProcedureResultAttribute bindings for result index {1}.", resultType.FullName, duplicates.Key)); } private static void BindMainResultMembers(object instance, object mainResult) { if (instance == null) return; IList bindings = GetMainResultBindings(instance.GetType()); if (bindings.Count == 0) return; if (bindings.Count > 1) throw new InvalidOperationException(string.Format("Type '{0}' defines multiple ProcedureResultAttribute bindings for the main procedure result.", instance.GetType().FullName)); ResultMemberBinding binding = bindings[0]; object value = ConvertScalarValue(binding.MemberType, mainResult == DBNull.Value ? null : mainResult); binding.SetValue(instance, value); } private static object ReadResultValue(Type propertyType, ProcedureResultAttribute attr, IDataReader reader) { Type elementType; if (propertyType == typeof(DataTable)) return reader.ToDataTable(string.IsNullOrEmpty(attr.Name) ? null : attr.Name); if (TryGetEnumerableElementType(propertyType, out elementType)) { if (elementType == typeof(object)) return reader.EnumerateReader().ToList(); if (elementType.IsValueType || elementType == typeof(string) || elementType == typeof(Guid)) return ReadSimpleList(propertyType, elementType, attr, reader); return ReadComplexList(propertyType, elementType, reader); } if (propertyType.IsValueType || propertyType == typeof(string) || propertyType == typeof(Guid)) return ReadSimpleValue(propertyType, attr, reader); return ReadComplexValue(propertyType, reader); } private static object ReadSimpleValue(Type propertyType, ProcedureResultAttribute attr, IDataReader reader) { object value = null; int ordinal = -1; bool haveRow = false; while (reader.Read()) { if (!haveRow) { ordinal = GetOrdinal(reader, attr); value = reader.IsDBNull(ordinal) ? null : reader[ordinal]; haveRow = true; } } if (!haveRow || value == null) return propertyType.GetDefaultValue(); return ConvertScalarValue(propertyType, value); } private static object ReadSimpleList(Type propertyType, Type elementType, ProcedureResultAttribute attr, IDataReader reader) { Type targetElementType = Nullable.GetUnderlyingType(elementType) ?? elementType; Type listType = typeof(List<>).MakeGenericType(elementType); var list = (System.Collections.IList)Activator.CreateInstance(listType); int ordinal = -1; bool initialized = false; while (reader.Read()) { if (!initialized) { ordinal = GetOrdinal(reader, attr); initialized = true; } if (reader.IsDBNull(ordinal)) { list.Add(elementType.GetDefaultValue()); continue; } object value = reader[ordinal]; if (targetElementType == typeof(Guid)) { Guid g; if (Guid.TryParse(value.ToString(), out g)) list.Add(elementType == typeof(Guid) ? (object)g : new Guid?(g)); else list.Add(elementType.GetDefaultValue()); } else if (targetElementType.IsEnum) list.Add(Enum.ToObject(targetElementType, value)); else list.Add(targetElementType == elementType ? elementType.CastObject(value) : targetElementType.CastObject(value)); } if (propertyType.IsArray) { Array array = Array.CreateInstance(elementType, list.Count); list.CopyTo(array, 0); return array; } return list; } private static object ReadComplexValue(Type propertyType, IDataReader reader) { object value = null; bool haveRow = false; while (reader.Read()) { if (!haveRow) { value = (reader.RowToDynamic() as object).Map(propertyType); haveRow = true; } } return value; } private static object ReadComplexList(Type propertyType, Type elementType, IDataReader reader) { Type listType = typeof(List<>).MakeGenericType(elementType); var list = (System.Collections.IList)Activator.CreateInstance(listType); while (reader.Read()) list.Add((reader.RowToDynamic() as object).Map(elementType)); if (propertyType.IsArray) { Array array = Array.CreateInstance(elementType, list.Count); list.CopyTo(array, 0); return array; } return list; } private static bool TryGetEnumerableElementType(Type type, out Type elementType) { elementType = null; if (type == typeof(string) || type == typeof(byte[])) return false; if (type.IsArray) { elementType = type.GetElementType(); return true; } if (type.IsGenericType) { Type generic = type.GetGenericTypeDefinition(); if (generic == typeof(List<>) || generic == typeof(IList<>) || generic == typeof(IEnumerable<>)) { elementType = type.GetGenericArguments()[0]; return true; } } Type enumerableInterface = type.GetInterfaces().FirstOrDefault(x => x.IsGenericType && x.GetGenericTypeDefinition() == typeof(IEnumerable<>)); if (enumerableInterface != null) { elementType = enumerableInterface.GetGenericArguments()[0]; return true; } return false; } private static int GetOrdinal(IDataReader reader, ProcedureResultAttribute attr) { if (attr == null || string.IsNullOrEmpty(attr.ColumnName)) return 0; return reader.GetOrdinal(attr.ColumnName); } private static bool IsMainResultBinding(ProcedureResultAttribute attribute) { return attribute != null && attribute.ResultIndex < 0; } private static object ConvertScalarValue(Type targetType, object value) { if (value == null) return targetType.GetDefaultValue(); Type underlyingType = Nullable.GetUnderlyingType(targetType) ?? targetType; if (underlyingType == typeof(Guid)) { Guid g; if (Guid.TryParse(value.ToString(), out g)) return targetType == typeof(Guid) ? (object)g : new Guid?(g); return targetType.GetDefaultValue(); } if (underlyingType.IsEnum) return Enum.ToObject(underlyingType, value); return underlyingType == targetType ? targetType.CastObject(value) : underlyingType.CastObject(value); } } /// Framework detection and specific implementations. public static class FrameworkTools { #region Mono or .NET Framework detection /// This is pretty simple trick. private static bool _isMono = Type.GetType("Mono.Runtime") != null; /// Gets a value indicating whether application is running under mono runtime. public static bool IsMono { get { return _isMono; } } #endregion Mono or .NET Framework detection static FrameworkTools() { _frameworkTypeArgumentsGetter = CreateTypeArgumentsGetter(); } #region GetGenericTypeArguments private static Func> _frameworkTypeArgumentsGetter = null; private static Func> CreateTypeArgumentsGetter() { // HACK: Creating binders assuming types are correct... this may fail. if (IsMono) { Type binderType = typeof(Microsoft.CSharp.RuntimeBinder.RuntimeBinderException).Assembly.GetType("Microsoft.CSharp.RuntimeBinder.CSharpInvokeMemberBinder"); if (binderType != null) { ParameterExpression param = Expression.Parameter(typeof(InvokeMemberBinder), "o"); try { return Expression.Lambda>>( Expression.TypeAs( Expression.Field( Expression.TypeAs(param, binderType), "typeArguments"), typeof(IList)), param).Compile(); } catch { } PropertyInfo prop = binderType.GetProperty("TypeArguments"); if (!prop.CanRead) return null; return Expression.Lambda>>( Expression.TypeAs( Expression.Property( Expression.TypeAs(param, binderType), prop.Name), typeof(IList)), param).Compile(); } } else { Type inter = typeof(Microsoft.CSharp.RuntimeBinder.RuntimeBinderException).Assembly.GetType("Microsoft.CSharp.RuntimeBinder.ICSharpInvokeOrInvokeMemberBinder"); if (inter != null) { PropertyInfo prop = inter.GetProperty("TypeArguments"); if (!prop.CanRead) return null; ParameterExpression objParm = Expression.Parameter(typeof(InvokeMemberBinder), "o"); return Expression.Lambda>>( Expression.TypeAs( Expression.Property( Expression.TypeAs(objParm, inter), prop.Name), typeof(IList)), objParm).Compile(); } } return null; } /// Extension method allowing to easily extract generic type /// arguments from assuming that it /// inherits from /// Microsoft.CSharp.RuntimeBinder.ICSharpInvokeOrInvokeMemberBinder /// in .NET Framework or /// Microsoft.CSharp.RuntimeBinder.CSharpInvokeMemberBinder /// under Mono. /// Binder from which get type arguments. /// This is generally a bad solution, but there is no other /// currently so we have to go with it. /// List of types passed as generic parameters. public static IList GetGenericTypeArguments(this InvokeMemberBinder binder) { // First try to use delegate if exist if (_frameworkTypeArgumentsGetter != null) return _frameworkTypeArgumentsGetter(binder); if (_isMono) { // HACK: Using Reflection // In mono this is trivial. // First we get field info. FieldInfo field = binder.GetType().GetField("typeArguments", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static); // If this was a success get and return it's value if (field != null) return field.GetValue(binder) as IList; else { PropertyInfo prop = binder.GetType().GetProperty("TypeArguments"); // If we have a property, return it's value if (prop != null) return prop.GetValue(binder, null) as IList; } } else { // HACK: Using Reflection // In this case, we need more aerobic :D // First, get the interface Type inter = binder.GetType().GetInterface("Microsoft.CSharp.RuntimeBinder.ICSharpInvokeOrInvokeMemberBinder"); if (inter != null) { // Now get property. PropertyInfo prop = inter.GetProperty("TypeArguments"); // If we have a property, return it's value if (prop != null) return prop.GetValue(binder, null) as IList; } } // Sadly return null if failed. return null; } #endregion GetGenericTypeArguments } /// Extends interface. public interface IExtendedDisposable : IDisposable { /// /// Gets a value indicating whether this instance is disposed. /// /// /// true if this instance is disposed; otherwise, false. /// bool IsDisposed { get; } } /// Extends interface. public interface IFinalizerDisposable : IExtendedDisposable { /// Performs application-defined tasks associated with /// freeing, releasing, or resetting unmanaged resources. /// If set to true dispose object. void Dispose(bool disposing); } public static class ReaderExtensions { public static bool? GetBooleanIfNotNull(this IDataReader r, string name, bool? def = null) { var ord = r.GetOrdinal(name); if (r.IsDBNull(ord)) return def; return r.GetBoolean(ord); } public static byte? GetByteIfNotNull(this IDataReader r, string name, byte? def = null) { var ord = r.GetOrdinal(name); if (r.IsDBNull(ord)) return def; return r.GetByte(ord); } public static char? GetCharIfNotNull(this IDataReader r, string name, char? def = null) { var ord = r.GetOrdinal(name); if (r.IsDBNull(ord)) return def; return r.GetChar(ord); } public static DateTime? GetDateTimeIfNotNull(this IDataReader r, string name, DateTime? def = null) { var ord = r.GetOrdinal(name); if (r.IsDBNull(ord)) return def; return r.GetDateTime(ord); } public static decimal? GetDecimalIfNotNull(this IDataReader r, string name, decimal? def = null) { var ord = r.GetOrdinal(name); if (r.IsDBNull(ord)) return def; return r.GetDecimal(ord); } public static double? GetDoubleIfNotNull(this IDataReader r, string name, double? def = null) { var ord = r.GetOrdinal(name); if (r.IsDBNull(ord)) return def; return r.GetDouble(ord); } public static float? GetFloatIfNotNull(this IDataReader r, string name, float? def = null) { var ord = r.GetOrdinal(name); if (r.IsDBNull(ord)) return def; return r.GetFloat(ord); } public static Guid? GetGuidIfNotNull(this IDataReader r, string name, Guid? def = null) { var ord = r.GetOrdinal(name); if (r.IsDBNull(ord)) return def; return r.GetGuid(ord); } public static short? GetInt16IfNotNull(this IDataReader r, string name, short? def = null) { var ord = r.GetOrdinal(name); if (r.IsDBNull(ord)) return def; return r.GetInt16(ord); } public static int? GetInt32IfNotNull(this IDataReader r, string name, int? def = null) { var ord = r.GetOrdinal(name); if (r.IsDBNull(ord)) return def; return r.GetInt32(ord); } public static long? GetInt64IfNotNull(this IDataReader r, string name, long? def = null) { var ord = r.GetOrdinal(name); if (r.IsDBNull(ord)) return def; return r.GetInt64(ord); } public static string GetStringIfNotNull(this IDataReader r, string name, string def = null) { var ord = r.GetOrdinal(name); if (r.IsDBNull(ord)) return def; return r.GetString(ord); } public static object GetValueIfNotNull(this IDataReader r, string name, object def = null) { var ord = r.GetOrdinal(name); if (r.IsDBNull(ord)) return def; return r.GetValue(ord); } } /// Class containing useful string extensions. internal static class StringExtensions { static StringExtensions() { InvalidMultipartMemberChars = _InvalidMultipartMemberChars.ToCharArray(); InvalidMemberChars = _InvalidMemberChars.ToCharArray(); } private static readonly string _InvalidMultipartMemberChars = " +-*/^%[]{}()!\"\\&=?¿"; private static readonly string _InvalidMemberChars = "." + _InvalidMultipartMemberChars; /// /// Gets an array with some invalid characters that cannot be used with multipart names for class members. /// public static char[] InvalidMultipartMemberChars { get; private set; } /// /// Gets an array with some invalid characters that cannot be used with names for class members. /// public static char[] InvalidMemberChars { get; private set; } /// /// Provides with an alternate and generic way to obtain an alternate string representation for this instance, /// applying the following rules: /// - Null values are returned as with the value, or a null object. /// - Enum values are translated into their string representation. /// - If the type has override the 'ToString' method then it is used. /// - If it is a dictionary, then a collection of key/value pairs where the value part is also translated. /// - If it is a collection, then a collection of value items also translated. /// - If it has public public properties (or if not, if it has public fields), the collection of name/value /// pairs, with the values translated. /// - Finally it falls back to the standard 'type.FullName' mechanism. /// /// The object to obtain its alternate string representation from. /// The brackets to use if needed. If not null it must be at least a 2-chars' array containing /// the opening and closing brackets. /// Representation of null string.. /// The alternate string representation of this object. public static string Sketch(this object obj, char[] brackets = null, string nullString = "(null)") { if (obj == null) return nullString; if (obj is string) return (string)obj; Type type = obj.GetType(); if (type.IsEnum) return obj.ToString(); // If the ToString() method has been overriden (by the type itself, or by its parents), let's use it... MethodInfo method = type.GetMethod("ToString", Type.EmptyTypes); if (method.DeclaringType != typeof(object)) return obj.ToString(); // For alll other cases... StringBuilder sb = new StringBuilder(); bool first = true; // Dictionaries... if (obj is IDictionary) { if (brackets == null || brackets.Length < 2) brackets = "[]".ToCharArray(); sb.AppendFormat("{0}", brackets[0]); first = true; foreach (DictionaryEntry kvp in (IDictionary)obj) { if (!first) sb.Append(", "); else first = false; sb.AppendFormat("'{0}'='{1}'", kvp.Key.Sketch(), kvp.Value.Sketch()); } sb.AppendFormat("{0}", brackets[1]); return sb.ToString(); } // IEnumerables... IEnumerator ator = null; if (obj is IEnumerable) ator = ((IEnumerable)obj).GetEnumerator(); else { method = type.GetMethod("GetEnumerator", Type.EmptyTypes); if (method != null) ator = (IEnumerator)method.Invoke(obj, null); } if (ator != null) { if (brackets == null || brackets.Length < 2) brackets = "[]".ToCharArray(); sb.AppendFormat("{0}", brackets[0]); first = true; while (ator.MoveNext()) { if (!first) sb.Append(", "); else first = false; sb.AppendFormat("{0}", ator.Current.Sketch()); } sb.AppendFormat("{0}", brackets[1]); if (ator is IDisposable) ((IDisposable)ator).Dispose(); return sb.ToString(); } // As a last resort, using the public properties (or fields if needed, or type name)... BindingFlags flags = BindingFlags.Public | BindingFlags.Instance | BindingFlags.FlattenHierarchy; PropertyInfo[] props = type.GetProperties(flags); FieldInfo[] infos = type.GetFields(flags); if (props.Length == 0 && infos.Length == 0) sb.Append(type.FullName); // Fallback if needed else { if (brackets == null || brackets.Length < 2) brackets = "{}".ToCharArray(); sb.AppendFormat("{0}", brackets[0]); first = true; if (props.Length != 0) { foreach (PropertyInfo prop in props) { if (!first) sb.Append(", "); else first = false; sb.AppendFormat("{0}='{1}'", prop.Name, prop.GetValue(obj, null).Sketch()); } } else { if (infos.Length != 0) { foreach (FieldInfo info in infos) { if (!first) sb.Append(", "); else first = false; sb.AppendFormat("{0}='{1}'", info.Name, info.GetValue(obj).Sketch()); } } } sb.AppendFormat("{0}", brackets[1]); } // And returning... return sb.ToString(); } /// /// Returns true if the target string contains any of the characters given. /// /// The target string. It cannot be null. /// An array containing the characters to test. It cannot be null. If empty false is returned. /// True if the target string contains any of the characters given, false otherwise. public static bool ContainsAny(this string source, char[] items) { if (source == null) throw new ArgumentNullException("source", "Source string cannot be null."); if (items == null) throw new ArgumentNullException("items", "Array of characters to test cannot be null."); if (items.Length == 0) return false; // No characters to validate int ix = source.IndexOfAny(items); return ix >= 0 ? true : false; } /// /// Returns a new validated string using the rules given. /// /// The source string. /// A description of the source string to build errors and exceptions if needed. /// True if the returned string can be null. /// True if the returned string can be empty. /// True to trim the returned string. /// True to left-trim the returned string. /// True to right-trim the returned string. /// If >= 0, the min valid length for the returned string. /// If >= 0, the max valid length for the returned string. /// If not '\0', the character to use to left-pad the returned string if needed. /// If not '\0', the character to use to right-pad the returned string if needed. /// If not null, an array containing invalid chars that must not appear in the returned /// string. /// If not null, an array containing the only characters that are considered valid for the /// returned string. /// A new validated string. public static string Validated(this string source, string desc = null, bool canbeNull = false, bool canbeEmpty = false, bool trim = true, bool trimStart = false, bool trimEnd = false, int minLen = -1, int maxLen = -1, char padLeft = '\0', char padRight = '\0', char[] invalidChars = null, char[] validChars = null) { // Assuring a valid descriptor... if (string.IsNullOrWhiteSpace(desc)) desc = "Source"; // Validating if null sources are accepted... if (source == null) { if (!canbeNull) throw new ArgumentNullException(desc, string.Format("{0} cannot be null.", desc)); return null; } // Trimming if needed... if (trim && !(trimStart || trimEnd)) source = source.Trim(); else { if (trimStart) source = source.TrimStart(' '); if (trimEnd) source = source.TrimEnd(' '); } // Adjusting lenght... if (minLen > 0) { if (padLeft != '\0') source = source.PadLeft(minLen, padLeft); if (padRight != '\0') source = source.PadRight(minLen, padRight); } if (maxLen > 0) { if (padLeft != '\0') source = source.PadLeft(maxLen, padLeft); if (padRight != '\0') source = source.PadRight(maxLen, padRight); } // Validating emptyness and lenghts... if (source.Length == 0) { if (!canbeEmpty) throw new ArgumentException(string.Format("{0} cannot be empty.", desc)); return string.Empty; } if (minLen >= 0 && source.Length < minLen) throw new ArgumentException(string.Format("Lenght of {0} '{1}' is lower than '{2}'.", desc, source, minLen)); if (maxLen >= 0 && source.Length > maxLen) throw new ArgumentException(string.Format("Lenght of {0} '{1}' is bigger than '{2}'.", desc, source, maxLen)); // Checking invalid chars... if (invalidChars != null) { int n = source.IndexOfAny(invalidChars); if (n >= 0) throw new ArgumentException(string.Format("Invalid character '{0}' found in {1} '{2}'.", source[n], desc, source)); } // Checking valid chars... if (validChars != null) { int n = validChars.ToString().IndexOfAny(source.ToCharArray()); if (n >= 0) throw new ArgumentException(string.Format("Invalid character '{0}' found in {1} '{2}'.", validChars.ToString()[n], desc, source)); } return source; } /// /// Splits the given string with the 'something AS alias' format, returning a tuple containing its 'something' and 'alias' parts. /// If no alias is detected, then its component in the tuple returned is null and all the contents from the source /// string are considered as the 'something' part. /// /// The source string. /// A tuple containing the 'something' and 'alias' parts. public static Tuple SplitSomethingAndAlias(this string source) { source = source.Validated("[Something AS Alias]"); string something = null; string alias = null; int n = source.LastIndexOf(" AS ", StringComparison.OrdinalIgnoreCase); if (n < 0) something = source; else { something = source.Substring(0, n); alias = source.Substring(n + 4); } return new Tuple(something, alias); } /// Allows to replace parameters inside of string. /// String containing parameters in format [$ParameterName]. /// Function that should return value that will be placed in string in place of placed parameter. /// Prefix of the parameter. This value can't be null or empty, default value [$. /// Suffix of the parameter. This value can't be null or empty, default value ]. /// Parsed string. public static string FillStringWithVariables(this string stringToFill, Func getValue, string prefix = "[$", string sufix = "]") { int startPos = 0, endPos = 0; prefix.Validated(); sufix.Validated(); startPos = stringToFill.IndexOf(prefix, startPos); while (startPos >= 0) { endPos = stringToFill.IndexOf(sufix, startPos + prefix.Length); int nextStartPos = stringToFill.IndexOf(prefix, startPos + prefix.Length); if (endPos > startPos + prefix.Length + 1 && (nextStartPos > endPos || nextStartPos == -1)) { string paramName = stringToFill.Substring(startPos + prefix.Length, endPos - (startPos + prefix.Length)); stringToFill = stringToFill .Remove(startPos, (endPos - startPos) + sufix.Length) .Insert(startPos, getValue(paramName)); } startPos = stringToFill.IndexOf(prefix, startPos + prefix.Length); } return stringToFill; } } /// Class contains unclassified extensions. internal static class UnclassifiedExtensions { /// Easy way to use conditional value. /// Includes . /// Input object type to check. /// Result type. /// The object to check. /// The select function. /// The else value. /// Selected value or default value. /// It lets you do this: /// var lname = thingy.NullOr(t => t.Name).NullOr(n => n.ToLower()); /// which is more fluent and (IMO) easier to read than this: /// var lname = (thingy != null ? thingy.Name : null) != null ? thingy.Name.ToLower() : null; /// public static R NullOr(this T obj, Func func, R elseValue = default(R)) where T : class { return obj != null && obj != DBNull.Value ? func(obj) : elseValue; } /// Easy way to use conditional value. /// Includes . /// Input object type to check. /// Result type. /// The object to check. /// The select function. /// The else value function. /// Selected value or default value. /// It lets you do this: /// var lname = thingy.NullOr(t => t.Name).NullOr(n => n.ToLower()); /// which is more fluent and (IMO) easier to read than this: /// var lname = (thingy != null ? thingy.Name : null) != null ? thingy.Name.ToLower() : null; /// public static R NullOrFn(this T obj, Func func, Func elseFunc = null) where T : class { // Old if to avoid recurency. return obj != null && obj != DBNull.Value ? func(obj) : elseFunc != null ? elseFunc() : default(R); } #if !NET6_0_OR_GREATER /// Simple distinct by selector extension. /// The enumerator of elements distinct by specified selector. /// Source collection. /// Distinct key selector. /// The enumerable element type parameter. /// The selector type parameter. public static IEnumerable DistinctBy(this IEnumerable source, Func keySelector) { HashSet seenKeys = new HashSet(); foreach (R element in source) if (seenKeys.Add(keySelector(element))) yield return element; } #endif } namespace Dynamics { /// /// Class able to parse dynamic lambda expressions. Allows to create dynamic logic. /// public class DynamicParser : IExtendedDisposable { #region Node /// /// Generic bindable operation where some of its operands is a dynamic argument, or a dynamic member or /// a method of that argument. /// [Serializable] public class Node : IDynamicMetaObjectProvider, IFinalizerDisposable, ISerializable { private DynamicParser _parser = null; #region MetaNode /// /// Represents the dynamic binding and a binding logic of /// an object participating in the dynamic binding. /// internal class MetaNode : DynamicMetaObject { /// /// Initializes a new instance of the class. /// /// The parameter. /// The restrictions. /// The value. public MetaNode(Expression parameter, BindingRestrictions rest, object value) : base(parameter, rest, value) { } // Func was cool but caused memory leaks private DynamicMetaObject GetBinder(Node node) { Node o = (Node)this.Value; node.Parser = o.Parser; o.Parser.Last = node; ParameterExpression p = Expression.Variable(typeof(Node), "ret"); BlockExpression exp = Expression.Block(new ParameterExpression[] { p }, Expression.Assign(p, Expression.Constant(node))); return new MetaNode(exp, this.Restrictions, node); } /// /// Performs the binding of the dynamic get member operation. /// /// An instance of the that represents the details of the dynamic operation. /// /// The new representing the result of the binding. /// public override DynamicMetaObject BindGetMember(GetMemberBinder binder) { return GetBinder(new GetMember((Node)this.Value, binder.Name)); } /// /// Performs the binding of the dynamic set member operation. /// /// An instance of the that represents the details of the dynamic operation. /// The representing the value for the set member operation. /// /// The new representing the result of the binding. /// public override DynamicMetaObject BindSetMember(SetMemberBinder binder, DynamicMetaObject value) { return GetBinder(new SetMember((Node)this.Value, binder.Name, value.Value)); } /// /// Performs the binding of the dynamic get index operation. /// /// An instance of the that represents the details of the dynamic operation. /// An array of instances - indexes for the get index operation. /// /// The new representing the result of the binding. /// public override DynamicMetaObject BindGetIndex(GetIndexBinder binder, DynamicMetaObject[] indexes) { return GetBinder(new GetIndex((Node)this.Value, MetaList2List(indexes))); } /// /// Performs the binding of the dynamic set index operation. /// /// An instance of the that represents the details of the dynamic operation. /// An array of instances - indexes for the set index operation. /// The representing the value for the set index operation. /// /// The new representing the result of the binding. /// public override DynamicMetaObject BindSetIndex(SetIndexBinder binder, DynamicMetaObject[] indexes, DynamicMetaObject value) { return GetBinder(new SetIndex((Node)this.Value, MetaList2List(indexes), value.Value)); } /// /// Performs the binding of the dynamic invoke operation. /// /// An instance of the that represents the details of the dynamic operation. /// An array of instances - arguments to the invoke operation. /// /// The new representing the result of the binding. /// public override DynamicMetaObject BindInvoke(InvokeBinder binder, DynamicMetaObject[] args) { return GetBinder(new Invoke((Node)this.Value, MetaList2List(args))); } /// /// Performs the binding of the dynamic invoke member operation. /// /// An instance of the that represents the details of the dynamic operation. /// An array of instances - arguments to the invoke member operation. /// /// The new representing the result of the binding. /// public override DynamicMetaObject BindInvokeMember(InvokeMemberBinder binder, DynamicMetaObject[] args) { return GetBinder(new Method((Node)this.Value, binder.Name, MetaList2List(args))); } /// /// Performs the binding of the dynamic binary operation. /// /// An instance of the that represents the details of the dynamic operation. /// An instance of the representing the right hand side of the binary operation. /// /// The new representing the result of the binding. /// public override DynamicMetaObject BindBinaryOperation(BinaryOperationBinder binder, DynamicMetaObject arg) { return GetBinder(new Binary((Node)this.Value, binder.Operation, arg.Value)); } /// /// Performs the binding of the dynamic unary operation. /// /// An instance of the that represents the details of the dynamic operation. /// /// The new representing the result of the binding. /// public override DynamicMetaObject BindUnaryOperation(UnaryOperationBinder binder) { Node o = (Node)this.Value; Unary node = new Unary(o, binder.Operation) { Parser = o.Parser }; o.Parser.Last = node; // If operation is 'IsTrue' or 'IsFalse', we will return false to keep the engine working... object ret = node; if (binder.Operation == ExpressionType.IsTrue) ret = (object)false; if (binder.Operation == ExpressionType.IsFalse) ret = (object)false; ParameterExpression p = Expression.Variable(ret.GetType(), "ret"); // the type is now obtained from "ret" BlockExpression exp = Expression.Block( new ParameterExpression[] { p }, Expression.Assign(p, Expression.Constant(ret))); // the expression is now obtained from "ret" return new MetaNode(exp, this.Restrictions, node); } /// /// Performs the binding of the dynamic conversion operation. /// /// An instance of the that represents the details of the dynamic operation. /// /// The new representing the result of the binding. /// public override DynamicMetaObject BindConvert(ConvertBinder binder) { Node o = (Node)this.Value; Convert node = new Convert(o, binder.ReturnType) { Parser = o.Parser }; o.Parser.Last = node; // Reducing the object to return if this is an assignment node... object ret = o; bool done = false; while (!done) { if (ret is SetMember) ret = ((SetMember)o).Value; else if (ret is SetIndex) ret = ((SetIndex)o).Value; else done = true; } // Creating an instance... if (binder.ReturnType == typeof(string)) ret = ret.ToString(); else { try { if (binder.ReturnType.IsNullableType()) ret = null; // to avoid cast exceptions else ret = Activator.CreateInstance(binder.ReturnType, true); // true to allow non-public ctor as well } catch { // as the last resort scenario ret = new object(); } } ParameterExpression p = Expression.Variable(binder.ReturnType, "ret"); BlockExpression exp = Expression.Block( new ParameterExpression[] { p }, Expression.Assign(p, Expression.Constant(ret, binder.ReturnType))); // specifying binder.ReturnType return new MetaNode(exp, this.Restrictions, node); } private static object[] MetaList2List(DynamicMetaObject[] metaObjects) { if (metaObjects == null) return null; object[] list = new object[metaObjects.Length]; for (int i = 0; i < metaObjects.Length; i++) list[i] = metaObjects[i].Value; return list; } } #endregion MetaNode #region Argument /// /// Describe a dynamic argument used in a dynamic lambda expression. /// [Serializable] public class Argument : Node, ISerializable { /// /// Initializes a new instance of the class. /// /// The name. public Argument(string name) : base(name) { } /// /// Initializes a new instance of the class. /// /// The info. /// The context. protected Argument(SerializationInfo info, StreamingContext context) : base(info, context) { } /// Returns a that represents this instance. /// A that represents this instance. public override string ToString() { if (IsDisposed) return "{DynamicParser::Node::Argument::Disposed}"; return Name; } } #endregion Argument #region GetMember /// /// Describe a 'get member' operation, as in 'x => x.Member'. /// [Serializable] public class GetMember : Node, ISerializable { /// /// Initializes a new instance of the class. /// /// The host. /// The name. public GetMember(Node host, string name) : base(host, name) { } /// /// Initializes a new instance of the class. /// /// The info. /// The context. protected GetMember(SerializationInfo info, StreamingContext context) : base(info, context) { } /// Returns a that represents this instance. /// A that represents this instance. public override string ToString() { if (IsDisposed) return "{DynamicParser::Node::GetMember::Disposed}"; return string.Format("{0}.{1}", Host.Sketch(), Name.Sketch()); } } #endregion GetMember #region SetMember /// /// Describe a 'set member' operation, as in 'x => x.Member = y'. /// [Serializable] public class SetMember : Node, ISerializable { /// /// Gets the value that has been (virtually) assigned to this member. It might be null if the null value has been /// assigned to this instance, or if this instance is disposed. /// public object Value { get; private set; } /// /// Initializes a new instance of the class. /// /// The host. /// The name. /// The value. public SetMember(Node host, string name, object value) : base(host, name) { Value = value; } /// /// Initializes a new instance of the class. /// /// The info. /// The context. protected SetMember(SerializationInfo info, StreamingContext context) : base(info, context) { string type = info.GetString("MemberType"); Value = type == "NULL" ? null : info.GetValue("MemberValue", Type.GetType(type)); } /// /// Gets the object data. /// /// The info. /// The context. public override void GetObjectData(SerializationInfo info, StreamingContext context) { info.AddValue("MemberType", Value == null ? "NULL" : Value.GetType().AssemblyQualifiedName); if (Value != null) info.AddValue("MemberValue", Value); base.GetObjectData(info, context); } /// Returns a that represents this instance. /// A that represents this instance. public override string ToString() { if (IsDisposed) return "{DynamicParser::Node::SetMember::Disposed}"; return string.Format("({0}.{1} = {2})", Host.Sketch(), Name.Sketch(), Value.Sketch()); } /// Performs application-defined tasks associated with /// freeing, releasing, or resetting unmanaged resources. /// If set to true dispose object. public override void Dispose(bool disposing) { if (disposing) { try { if (Value != null) { var node = Value as Node; if (node != null) { if (node.IsNodeAncestor(this)) node.Host = null; node.Dispose(disposing); } Value = null; } } catch { } } base.Dispose(disposing); } } #endregion SetMember #region GetIndex /// /// Describe a 'get indexed' operation, as in 'x => x.Member[...]'. /// [Serializable] public class GetIndex : Node, ISerializable { /// Gets the indexes. public object[] Indexes { get; internal set; } /// /// Initializes a new instance of the class. /// /// The host. /// The indexes. /// Indexes array cannot be null. /// Indexes array cannot be empty. public GetIndex(Node host, object[] indexes) : base(host) { if (indexes == null) throw new ArgumentNullException("indexes", "Indexes array cannot be null."); if (indexes.Length == 0) throw new ArgumentException("Indexes array cannot be empty."); Indexes = indexes; } /// /// Initializes a new instance of the class. /// /// The info. /// The context. protected GetIndex(SerializationInfo info, StreamingContext context) : base(info, context) { int count = (int)info.GetValue("IndexCount", typeof(int)); if (count != 0) { Indexes = new object[count]; for (int i = 0; i < count; i++) { string typeName = info.GetString("IndexType" + i); object obj = typeName == "NULL" ? null : info.GetValue("IndexValue" + i, Type.GetType(typeName)); Indexes[i] = obj; } } } /// /// Gets the object data. /// /// The info. /// The context. public override void GetObjectData(SerializationInfo info, StreamingContext context) { int count = Indexes == null ? 0 : Indexes.Length; info.AddValue("IndexCount", count); for (int i = 0; i < count; i++) { info.AddValue("IndexType" + i, Indexes[i] == null ? "NULL" : Indexes[i].GetType().AssemblyQualifiedName); if (Indexes[i] != null) info.AddValue("IndexValue" + i, Indexes[i]); } base.GetObjectData(info, context); } /// Returns a that represents this instance. /// A that represents this instance. public override string ToString() { if (IsDisposed) return "{DynamicParser::Node::GetIndex::Disposed}"; return string.Format("{0}{1}", Host.Sketch(), Indexes == null ? "[empty]" : Indexes.Sketch()); } /// Performs application-defined tasks associated with /// freeing, releasing, or resetting unmanaged resources. /// If set to true dispose object. public override void Dispose(bool disposing) { if (disposing) { try { if (Indexes != null) { for (int i = 0; i < Indexes.Length; i++) { var node = Indexes[i] as Node; if (node != null) { if (node.IsNodeAncestor(this)) node.Host = null; node.Dispose(disposing); } } Array.Clear(Indexes, 0, Indexes.Length); } } catch { } Indexes = null; } base.Dispose(disposing); } } #endregion GetIndex #region SetIndex /// /// Describe a 'set indexed' operation, as in 'x => x.Member[...] = Value'. /// [Serializable] public class SetIndex : GetIndex, ISerializable { /// /// Gets the value that has been (virtually) assigned to this member. It might be null if the null value has been /// assigned to this instance, or if this instance is disposed. /// public object Value { get; private set; } /// /// Initializes a new instance of the class. /// /// The host. /// The indexes. /// The value. public SetIndex(Node host, object[] indexes, object value) : base(host, indexes) { Value = value; } /// /// Initializes a new instance of the class. /// /// The info. /// The context. protected SetIndex(SerializationInfo info, StreamingContext context) : base(info, context) { string type = info.GetString("MemberType"); Value = type == "NULL" ? null : info.GetValue("MemberValue", Type.GetType(type)); } /// /// Gets the object data. /// /// The info. /// The context. public override void GetObjectData(SerializationInfo info, StreamingContext context) { info.AddValue("MemberType", Value == null ? "NULL" : Value.GetType().AssemblyQualifiedName); if (Value != null) info.AddValue("MemberValue", Value); base.GetObjectData(info, context); } /// Returns a that represents this instance. /// A that represents this instance. public override string ToString() { if (IsDisposed) return "{DynamicParser::Node::SetIndex::Disposed}"; return string.Format("({0}{1} = {2})", Host.Sketch(), Indexes == null ? "[empty]" : Indexes.Sketch(), Value.Sketch()); } /// Performs application-defined tasks associated with /// freeing, releasing, or resetting unmanaged resources. /// If set to true dispose object. public override void Dispose(bool disposing) { if (disposing) { try { if (Value != null) { var node = Value as Node; if (node != null) { if (node.IsNodeAncestor(this)) node.Host = null; node.Dispose(disposing); } Value = null; } } catch { } } base.Dispose(disposing); } } #endregion SetIndex #region Invoke /// /// Describe a method invocation operation, as in 'x => x.Method(...)". /// [Serializable] public class Invoke : Node, ISerializable { /// Gets the arguments. public object[] Arguments { get; internal set; } /// /// Initializes a new instance of the class. /// /// The host. /// The arguments. public Invoke(Node host, object[] arguments) : base(host) { Arguments = arguments == null || arguments.Length == 0 ? null : arguments; } /// /// Initializes a new instance of the class. /// /// The info. /// The context. protected Invoke(SerializationInfo info, StreamingContext context) : base(info, context) { int count = (int)info.GetValue("ArgumentCount", typeof(int)); if (count != 0) { Arguments = new object[count]; for (int i = 0; i < count; i++) { string typeName = info.GetString("ArgumentType" + i); object obj = typeName == "NULL" ? null : info.GetValue("ArgumentValue" + i, Type.GetType(typeName)); Arguments[i] = obj; } } } /// /// Gets the object data. /// /// The info. /// The context. public override void GetObjectData(SerializationInfo info, StreamingContext context) { int count = Arguments == null ? 0 : Arguments.Length; info.AddValue("ArgumentCount", count); for (int i = 0; i < count; i++) { info.AddValue("ArgumentType" + i, Arguments[i] == null ? "NULL" : Arguments[i].GetType().AssemblyQualifiedName); if (Arguments[i] != null) info.AddValue("ArgumentValue" + i, Arguments[i]); } base.GetObjectData(info, context); } /// Performs application-defined tasks associated with /// freeing, releasing, or resetting unmanaged resources. /// If set to true dispose object. public override void Dispose(bool disposing) { if (disposing) { try { if (Arguments != null) { for (int i = 0; i < Arguments.Length; i++) { var node = Arguments[i] as Node; if (node != null) { if (node.IsNodeAncestor(this)) node.Host = null; node.Dispose(disposing); } } Array.Clear(Arguments, 0, Arguments.Length); } } catch { } Arguments = null; } base.Dispose(disposing); } /// Returns a that represents this instance. /// A that represents this instance. public override string ToString() { if (IsDisposed) return "{DynamicParser::Node::Invoke::Disposed}"; return string.Format("{0}{1}", Host.Sketch(), Arguments == null ? "()" : Arguments.Sketch(brackets: "()".ToCharArray())); } } #endregion Invoke #region Method /// /// Describe a method invocation operation, as in 'x => x.Method(...)". /// [Serializable] public class Method : Node, ISerializable { /// Gets the arguments. public object[] Arguments { get; internal set; } /// /// Initializes a new instance of the class. /// /// The host. /// The name. /// The arguments. public Method(Node host, string name, object[] arguments) : base(host, name) { Arguments = arguments == null || arguments.Length == 0 ? null : arguments; } /// /// Initializes a new instance of the class. /// /// The info. /// The context. protected Method(SerializationInfo info, StreamingContext context) : base(info, context) { int count = (int)info.GetValue("ArgumentCount", typeof(int)); if (count != 0) { Arguments = new object[count]; for (int i = 0; i < count; i++) { string typeName = info.GetString("ArgumentType" + i); object obj = typeName == "NULL" ? null : info.GetValue("ArgumentValue" + i, Type.GetType(typeName)); Arguments[i] = obj; } } } /// /// Gets the object data. /// /// The info. /// The context. public override void GetObjectData(SerializationInfo info, StreamingContext context) { int count = Arguments == null ? 0 : Arguments.Length; info.AddValue("ArgumentCount", count); for (int i = 0; i < count; i++) { info.AddValue("ArgumentType" + i, Arguments[i] == null ? "NULL" : Arguments[i].GetType().AssemblyQualifiedName); if (Arguments[i] != null) info.AddValue("ArgumentValue" + i, Arguments[i]); } base.GetObjectData(info, context); } /// Returns a that represents this instance. /// A that represents this instance. public override string ToString() { if (IsDisposed) return "{DynamicParser::Node::Method::Disposed}"; return string.Format("{0}.{1}{2}", Host.Sketch(), Name.Sketch(), Arguments == null ? "()" : Arguments.Sketch(brackets: "()".ToCharArray())); } /// Performs application-defined tasks associated with /// freeing, releasing, or resetting unmanaged resources. /// If set to true dispose object. public override void Dispose(bool disposing) { if (disposing) { try { if (Arguments != null) { for (int i = 0; i < Arguments.Length; i++) { var node = Arguments[i] as Node; if (node != null) { if (node.IsNodeAncestor(this)) node.Host = null; node.Dispose(disposing); } } Array.Clear(Arguments, 0, Arguments.Length); } } catch { } Arguments = null; } base.Dispose(disposing); } } #endregion Method #region Binary /// /// Represents a binary operation between a dynamic element and an arbitrary object, including null ones, as in /// 'x => (x && null)'. The left operand must be an instance of , whereas the right one /// can be any object, including null values. /// [Serializable] public class Binary : Node, ISerializable { /// Gets the operation. public ExpressionType Operation { get; private set; } /// Gets host of the . public Node Left { get { return Host; } } /// Gets the right side value. public object Right { get; private set; } /// /// Initializes a new instance of the class. /// /// The left. /// The operation. /// The right. public Binary(Node left, ExpressionType operation, object right) : base(left) { Operation = operation; Right = right; } /// /// Initializes a new instance of the class. /// /// The info. /// The context. protected Binary(SerializationInfo info, StreamingContext context) : base(info, context) { Operation = (ExpressionType)info.GetValue("Operation", typeof(ExpressionType)); string type = info.GetString("RightType"); Right = type == "NULL" ? null : (Node)info.GetValue("RightItem", Type.GetType(type)); } /// /// Gets the object data. /// /// The info. /// The context. public override void GetObjectData(SerializationInfo info, StreamingContext context) { info.AddValue("Operation", Operation); info.AddValue("RightType", Right == null ? "NULL" : Right.GetType().AssemblyQualifiedName); if (Right != null) info.AddValue("RightItem", Right); base.GetObjectData(info, context); } /// Returns a that represents this instance. /// A that represents this instance. public override string ToString() { if (IsDisposed) return "{DynamicParser::Node::Binary::Disposed}"; return string.Format("({0} {1} {2})", Host.Sketch(), Operation, Right.Sketch()); } /// Performs application-defined tasks associated with /// freeing, releasing, or resetting unmanaged resources. /// If set to true dispose object. public override void Dispose(bool disposing) { base.Dispose(disposing); if (disposing) { if (Left != null) { if (Left is Node) { Node n = (Node)Left; if (!n.IsDisposed) n.Dispose(disposing); } } if (Right != null) { if (Right is Node) { Node n = (Node)Right; if (!n.IsDisposed) n.Dispose(disposing); } Right = null; } } } } #endregion Binary #region Unary /// /// Represents an unary operation, as in 'x => !x'. The target must be a instance. There /// is no distinction between pre- and post- version of the same operation. /// [Serializable] public class Unary : Node, ISerializable { /// Gets the operation. public ExpressionType Operation { get; private set; } /// Gets host of the . public Node Target { get; private set; } /// /// Initializes a new instance of the class. /// /// The target. /// The operation. public Unary(Node target, ExpressionType operation) : base(target) { Operation = operation; Target = target; } /// /// Initializes a new instance of the class. /// /// The info. /// The context. protected Unary(SerializationInfo info, StreamingContext context) : base(info, context) { Operation = (ExpressionType)info.GetValue("Operation", typeof(ExpressionType)); } /// /// Gets the object data. /// /// The info. /// The context. public override void GetObjectData(SerializationInfo info, StreamingContext context) { info.AddValue("Operation", Operation); base.GetObjectData(info, context); } /// Returns a that represents this instance. /// A that represents this instance. public override string ToString() { if (IsDisposed) return "{DynamicParser::Node::Binary::Disposed}"; return string.Format("({0} {1})", Operation, Host.Sketch()); } /// Performs application-defined tasks associated with /// freeing, releasing, or resetting unmanaged resources. /// If set to true dispose object. public override void Dispose(bool disposing) { if (disposing) { try { if (Target != null) { var node = Target as Node; if (node != null) { if (node.IsNodeAncestor(this)) node.Host = null; node.Dispose(disposing); } Target = null; } } catch { } } base.Dispose(disposing); } } #endregion Unary #region Convert /// /// Represents a conversion operation, as in 'x => (string)x'. /// [Serializable] public class Convert : Node, ISerializable { /// Gets the new type to which value will be converted. public Type NewType { get; private set; } /// Gets host of the . public Node Target { get { return Host; } } /// /// Initializes a new instance of the class. /// /// The target. /// The new type. public Convert(Node target, Type newType) : base(target) { NewType = newType; } /// /// Initializes a new instance of the class. /// /// The info. /// The context. protected Convert(SerializationInfo info, StreamingContext context) : base(info, context) { NewType = (Type)info.GetValue("NewType", typeof(Type)); } /// /// Gets the object data. /// /// The info. /// The context. public override void GetObjectData(SerializationInfo info, StreamingContext context) { info.AddValue("NewType", NewType); base.GetObjectData(info, context); } } #endregion Convert /// /// Gets the name of the member. It might be null if this instance is disposed. /// public string Name { get; internal set; } /// Gets host of the . public Node Host { get; internal set; } /// Gets reference to the parser. public DynamicParser Parser { get { return _parser; } internal set { _parser = value; if (_parser != null) _parser._allNodes.Add(this); } } /// /// Initializes a new instance of the class. /// internal Node() { IsDisposed = false; } /// /// Initializes a new instance of the class. /// /// The host. internal Node(Node host) : this() { if (host == null) throw new ArgumentNullException("host", "Host cannot be null."); Host = host; } /// /// Initializes a new instance of the class. /// /// The name. internal Node(string name) : this() { Name = name.Validated("Name"); } /// /// Initializes a new instance of the class. /// /// The host. /// The name. /// Host cannot be null. internal Node(Node host, string name) : this(host) { Name = name.Validated("Name"); } /// /// Initializes a new instance of the class. /// /// The info. /// The context. protected Node(SerializationInfo info, StreamingContext context) { Name = info.GetString("MemberName"); string type = info.GetString("HostType"); Host = type == "NULL" ? null : (Node)info.GetValue("HostItem", Type.GetType(type)); } /// Returns whether the given node is an ancestor of this instance. /// The node to test. /// True if the given node is an ancestor of this instance. public bool IsNodeAncestor(Node node) { if (node != null) { Node parent = Host; while (parent != null) { if (object.ReferenceEquals(parent, node)) return true; parent = parent.Host; } } return false; } /// Returns a that represents this instance. /// A that represents this instance. public override string ToString() { if (IsDisposed) return "{DynamicParser::Node::Disposed}"; return "{DynamicParser::Node::Empty}"; } #region Implementation of IDynamicMetaObjectProvider /// Returns the responsible /// for binding operations performed on this object. /// The expression tree representation of the runtime value. /// The to bind this object. /// Thrown if this instance is disposed. public DynamicMetaObject GetMetaObject(System.Linq.Expressions.Expression parameter) { if (IsDisposed) throw new ObjectDisposedException("DynamicParser.Node"); return new MetaNode( parameter, BindingRestrictions.GetInstanceRestriction(parameter, this), this); } #endregion Implementation of IDynamicMetaObjectProvider #region Implementation of IFinalizerDisposable /// Finalizes an instance of the class. ~Node() { Dispose(false); } /// Gets a value indicating whether this instance is disposed. public bool IsDisposed { get; private set; } /// Performs application-defined tasks associated with /// freeing, releasing, or resetting unmanaged resources. public virtual void Dispose() { Dispose(true); GC.SuppressFinalize(this); } /// Performs application-defined tasks associated with /// freeing, releasing, or resetting unmanaged resources. /// If set to true dispose object. public virtual void Dispose(bool disposing) { if (disposing) { IsDisposed = true; if (Host != null && !Host.IsDisposed) Host.Dispose(); Host = null; Parser = null; } } #endregion Implementation of IFinalizerDisposable #region Implementation of ISerializable /// /// Populates a with the data needed to serialize the target object. /// /// The to populate with data. /// The destination (see ) for this serialization. public virtual void GetObjectData(SerializationInfo info, StreamingContext context) { if (!string.IsNullOrEmpty(Name)) info.AddValue("MemberName", Name); info.AddValue("HostType", Host == null ? "NULL" : Host.GetType().AssemblyQualifiedName); if (Host != null) info.AddValue("HostItem", Host); } #endregion Implementation of ISerializable } #endregion Node #region Data private List _arguments = new List(); private List _allNodes = new List(); private object _uncertainResult; #endregion Data #region Properties /// Gets the last node (root of the tree). public Node Last { get; internal set; } /// /// Gets an enumeration containing the dynamic arguments used in the dynamic lambda expression parsed. /// public IEnumerable Arguments { get { List list = new List(); if (!IsDisposed && _arguments != null) list.AddRange(_arguments); foreach (Node.Argument arg in list) yield return arg; list.Clear(); list = null; } } /// /// Gets the number of dynamic arguments used in the dynamic lambda expression parsed. /// public int Count { get { return _arguments == null ? 0 : _arguments.Count; } } /// /// Gets the result of the parsing of the dynamic lambda expression. This result can be either an arbitrary object, /// including null, if the expression resolves to it, or an instance of the class that /// contains the last logic expression evaluated when parsing the dynamic lambda expression. /// public object Result { get { return _uncertainResult ?? Last; } } #endregion Properties private DynamicParser(Delegate f) { // I know this can be almost a one liner // but it causes memory leaks when so. ParameterInfo[] pars = f.Method.GetParameters(); foreach (ParameterInfo p in pars) { int attrs = p.GetCustomAttributes(typeof(DynamicAttribute), true).Length; if (attrs != 0) { Node.Argument par = new Node.Argument(p.Name) { Parser = this }; this._arguments.Add(par); } else throw new ArgumentException(string.Format("Argument '{0}' must be dynamic.", p.Name)); } try { _uncertainResult = f.DynamicInvoke(_arguments.ToArray()); } catch (TargetInvocationException e) { if (e.InnerException != null) throw e.InnerException; throw; } } /// /// Parses the dynamic lambda expression given in the form of a delegate, and returns a new instance of the /// class that holds the dynamic arguments used in the dynamic lambda expression, and /// the result of the parsing. /// /// The dynamic lambda expression to parse. /// A new instance of . public static DynamicParser Parse(Delegate f) { return new DynamicParser(f); } /// Returns a that represents this instance. /// A that represents this instance. public override string ToString() { if (IsDisposed) return "{DynamicParser::Disposed}"; StringBuilder sb = new StringBuilder(); sb.Append("("); bool first = true; if (_arguments != null) { foreach (Node.Argument arg in _arguments) { if (!first) sb.Append(", "); else first = false; sb.Append(arg); } } sb.Append(")"); sb.AppendFormat(" => {0}", Result.Sketch()); return sb.ToString(); } #region Implementation of IExtendedDisposable /// Gets a value indicating whether this instance is disposed. public bool IsDisposed { get; private set; } /// /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. /// public void Dispose() { if (IsDisposed) return; IsDisposed = true; if (_uncertainResult != null) { if (_uncertainResult is Node) ((Node)_uncertainResult).Dispose(); _uncertainResult = null; } if (Last != null) { if (!Last.IsDisposed) Last.Dispose(); Last = null; } if (_arguments != null) { _arguments.ForEach(x => { if (!x.IsDisposed) x.Dispose(); }); _arguments.Clear(); _arguments = null; } if (_allNodes != null) { _allNodes.ForEach(x => { if (!x.IsDisposed) x.Dispose(); }); _allNodes.Clear(); _allNodes = null; } } #endregion Implementation of IExtendedDisposable } /// Class that allows to use interfaces as dynamic objects. /// Type of class to proxy. /// This is temporary solution. Which allows to use builders as a dynamic type. public class DynamicProxy : DynamicObject, IDisposable { private T _proxy; private Type _type; private Dictionary _properties; private Dictionary _methods; /// /// Initializes a new instance of the class. /// /// The object to which proxy should be created. /// The object to which proxy should be created is null. public DynamicProxy(T proxiedObject) { if (proxiedObject == null) throw new ArgumentNullException("proxiedObject"); _proxy = proxiedObject; _type = typeof(T); DynamicTypeMap mapper = Mapper.DynamicMapperCache.GetMapper(); _properties = mapper .ColumnsMap .ToDictionary( k => k.Value.Name, v => v.Value); _methods = GetAllMembers(_type) .Where(x => x is MethodInfo) .Cast() .Where(m => !((m.Name.StartsWith("set_") && m.ReturnType == typeof(void)) || m.Name.StartsWith("get_"))) .Where(m => !m.IsStatic && !m.IsGenericMethod) .ToDictionary( k => k, v => { try { Type type = v.ReturnType == typeof(void) ? Expression.GetActionType(v.GetParameters().Select(t => t.ParameterType).ToArray()) : Expression.GetDelegateType(v.GetParameters().Select(t => t.ParameterType).Concat(new[] { v.ReturnType }).ToArray()); return Delegate.CreateDelegate(type, _proxy, v.Name); } catch (ArgumentException) { return null; } }); } /// Provides implementation for type conversion operations. /// Classes derived from the /// class can override this method to specify dynamic behavior for /// operations that convert an object from one type to another. /// Provides information about the conversion operation. /// The binder.Type property provides the type to which the object must be /// converted. For example, for the statement (String)sampleObject in C# /// (CType(sampleObject, Type) in Visual Basic), where sampleObject is an /// instance of the class derived from the /// class, binder.Type returns the type. /// The binder.Explicit property provides information about the kind of /// conversion that occurs. It returns true for explicit conversion and /// false for implicit conversion. /// The result of the type conversion operation. /// Returns true if the operation is successful; otherwise, false. /// If this method returns false, the run-time binder of the language determines the /// behavior. (In most cases, a language-specific run-time exception is thrown). public override bool TryConvert(ConvertBinder binder, out object result) { if (binder.Type == typeof(T)) { result = _proxy; return true; } if (_proxy != null && binder.Type.IsAssignableFrom(_proxy.GetType())) { result = _proxy; return true; } return base.TryConvert(binder, out result); } /// Provides the implementation for operations that get member /// values. Classes derived from the /// class can override this method to specify dynamic behavior for /// operations such as getting a value for a property. /// Provides information about the object that /// called the dynamic operation. The binder.Name property provides /// the name of the member on which the dynamic operation is performed. /// For example, for the Console.WriteLine(sampleObject.SampleProperty) /// statement, where sampleObject is an instance of the class derived /// from the class, /// binder.Name returns "SampleProperty". The binder.IgnoreCase property /// specifies whether the member name is case-sensitive. /// The result of the get operation. For example, /// if the method is called for a property, you can assign the property /// value to . /// Returns true if the operation is successful; otherwise, /// false. If this method returns false, the run-time binder of the /// language determines the behavior. (In most cases, a run-time exception /// is thrown). public override bool TryGetMember(GetMemberBinder binder, out object result) { try { DynamicPropertyInvoker prop = _properties.TryGetValue(binder.Name); result = prop.NullOr(p => p.Get.NullOr(g => g(_proxy), null), null); return prop != null && prop.Get != null; } catch (Exception ex) { throw new InvalidOperationException(string.Format("Cannot get member {0}", binder.Name), ex); } } /// Provides the implementation for operations that set member /// values. Classes derived from the /// class can override this method to specify dynamic behavior for operations /// such as setting a value for a property. /// Provides information about the object that called /// the dynamic operation. The binder.Name property provides the name of /// the member to which the value is being assigned. For example, for the /// statement sampleObject.SampleProperty = "Test", where sampleObject is /// an instance of the class derived from the /// class, binder.Name returns "SampleProperty". The binder.IgnoreCase /// property specifies whether the member name is case-sensitive. /// The value to set to the member. For example, for /// sampleObject.SampleProperty = "Test", where sampleObject is an instance /// of the class derived from the /// class, the is "Test". /// Returns true if the operation is successful; otherwise, /// false. If this method returns false, the run-time binder of the /// language determines the behavior. (In most cases, a language-specific /// run-time exception is thrown). public override bool TrySetMember(SetMemberBinder binder, object value) { try { DynamicPropertyInvoker prop = _properties.TryGetValue(binder.Name); if (prop != null && prop.Setter != null) { prop.Set(_proxy, value); return true; } return false; } catch (Exception ex) { throw new InvalidOperationException(string.Format("Cannot set member {0} to '{1}'", binder.Name, value), ex); } } /// Provides the implementation for operations that invoke a member. /// Classes derived from the /// class can override this method to specify dynamic behavior for /// operations such as calling a method. /// Provides information about the dynamic operation. /// The binder.Name property provides the name of the member on which the /// dynamic operation is performed. For example, for the statement /// sampleObject.SampleMethod(100), where sampleObject is an instance of /// the class derived from the /// class, binder.Name returns "SampleMethod". The binder.IgnoreCase property /// specifies whether the member name is case-sensitive. /// The arguments that are passed to the object member /// during the invoke operation. For example, for the statement /// sampleObject.SampleMethod(100), where sampleObject is derived from the /// class, /// First element of is equal to 100. /// The result of the member invocation. /// Returns true if the operation is successful; otherwise, /// false. If this method returns false, the run-time binder of the /// language determines the behavior. (In most cases, a language-specific /// run-time exception is thrown). public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result) { return TryInvokeMethod(binder.Name, out result, args) || base.TryInvokeMember(binder, args, out result); } private bool TryInvokeMethod(string name, out object result, object[] args) { result = null; MethodInfo mi = _methods.Keys .Where(m => m.Name == name) .FirstOrDefault(m => CompareTypes(m.GetParameters().ToArray(), args.Select(a => a.GetType()).ToArray())); Delegate d = _methods.TryGetValue(mi); if (d != null) { result = d.DynamicInvoke(CompleteArguments(mi.GetParameters().ToArray(), args)); if (d.Method.ReturnType == _type && result is T) result = new DynamicProxy((T)result); return true; } else if (mi != null) { result = mi.Invoke(_proxy, CompleteArguments(mi.GetParameters().ToArray(), args)); if (mi.ReturnType == _type && result is T) result = new DynamicProxy((T)result); return true; } return false; } private bool CompareTypes(ParameterInfo[] parameters, Type[] types) { if (parameters.Length < types.Length || parameters.Count(p => !p.IsOptional) > types.Length) return false; for (int i = 0; i < types.Length; i++) if (types[i] != parameters[i].ParameterType && !parameters[i].ParameterType.IsAssignableFrom(types[i])) return false; return true; } private object[] CompleteArguments(ParameterInfo[] parameters, object[] arguments) { return arguments.Concat(parameters.Skip(arguments.Length).Select(p => p.DefaultValue)).ToArray(); } private IEnumerable GetAllMembers(Type type) { if (type.IsInterface) { List members = new List(); List considered = new List(); Queue queue = new Queue(); considered.Add(type); queue.Enqueue(type); while (queue.Count > 0) { Type subType = queue.Dequeue(); foreach (Type subInterface in subType.GetInterfaces()) { if (considered.Contains(subInterface)) continue; considered.Add(subInterface); queue.Enqueue(subInterface); } MemberInfo[] typeProperties = subType.GetMembers( BindingFlags.FlattenHierarchy | BindingFlags.Public | BindingFlags.Instance); IEnumerable newPropertyInfos = typeProperties .Where(x => !members.Contains(x)); members.InsertRange(0, newPropertyInfos); } return members; } return type.GetMembers(BindingFlags.FlattenHierarchy | BindingFlags.Public | BindingFlags.Instance); } /// Performs application-defined tasks associated with /// freeing, releasing, or resetting unmanaged resources. public void Dispose() { object res; TryInvokeMethod("Dispose", out res, new object[] { }); _properties.Clear(); _methods = null; _properties = null; _type = null; _proxy = default(T); } } } } namespace Mapper { /// Allows to add table name to class. [AttributeUsage(AttributeTargets.Property)] public class ColumnAttribute : Attribute { /// Gets or sets name. public string Name { get; set; } /// Gets or sets column type. /// Used when overriding schema. public DbType? Type { get; set; } /// Gets or sets a value indicating whether column is a key. public bool IsKey { get; set; } /// Gets or sets a value indicating whether column allows null or not. /// Information only. public bool AllowNull { get; set; } /// Gets or sets a value indicating whether column should have unique value. /// Used when overriding schema. public bool? IsUnique { get; set; } /// Gets or sets column size. /// Used when overriding schema. public int? Size { get; set; } /// Gets or sets column precision. /// Used when overriding schema. public byte? Precision { get; set; } /// Gets or sets column scale. /// Used when overriding schema. public byte? Scale { get; set; } /// Gets or sets a value indicating whether this column is no allowed to be inserted. /// This is only a suggestion to automated mapping. public bool IsNoInsert { get; set; } /// Gets or sets a value indicating whether this column is no allowed to be updated. /// This is only a suggestion to automated mapping. public bool IsNoUpdate { get; set; } #region Constructors /// Initializes a new instance of the class. public ColumnAttribute() { AllowNull = true; } /// Initializes a new instance of the class. /// Name of column. public ColumnAttribute(string name) : this() { Name = name; } /// Initializes a new instance of the class. /// Set column as a key column. public ColumnAttribute(bool isKey) : this() { IsKey = isKey; } /// Initializes a new instance of the class. /// Name of column. /// Set column as a key column. public ColumnAttribute(string name, bool isKey) : this(name) { IsKey = isKey; } /// Initializes a new instance of the class. /// Set column as a key column. /// Set column type. public ColumnAttribute(bool isKey, DbType type) : this(isKey) { Type = type; } /// Initializes a new instance of the class. /// Name of column. /// Set column as a key column. /// Set column type. public ColumnAttribute(string name, bool isKey, DbType type) : this(name, isKey) { Type = type; } /// Initializes a new instance of the class. /// Name of column. /// Set column as a key column. /// Set column type. /// Set column value size. public ColumnAttribute(string name, bool isKey, DbType type, int size) : this(name, isKey, type) { Size = size; } /// Initializes a new instance of the class. /// Name of column. /// Set column as a key column. /// Set column type. /// Set column value precision. /// Set column value scale. public ColumnAttribute(string name, bool isKey, DbType type, byte precision, byte scale) : this(name, isKey, type) { Precision = precision; Scale = scale; } /// Initializes a new instance of the class. /// Name of column. /// Set column as a key column. /// Set column type. /// Set column value size. /// Set column value precision. /// Set column value scale. public ColumnAttribute(string name, bool isKey, DbType type, int size, byte precision, byte scale) : this(name, isKey, type, precision, scale) { Size = size; } /// Initializes a new instance of the class. /// Name of column. /// Set column as a key column. /// Set column has unique value. /// Set column type. /// Set column value size. /// Set column value precision. /// Set column value scale. public ColumnAttribute(string name, bool isKey, bool isUnique, DbType type, int size, byte precision, byte scale) : this(name, isKey, type, size, precision, scale) { IsUnique = isUnique; } #endregion Constructors } /// Type cast helper. public static class DynamicCast { /// Gets the default value. /// The type. /// Default instance. public static object GetDefaultValue(this Type type) { return type.IsValueType ? TypeDefaults.GetOrAdd(type, t => Activator.CreateInstance(t)) : null; } /// Casts the object to this type. /// The type to which cast value. /// The value to cast. /// Value casted to new type. public static object CastObject(this Type type, object val) { return GetConverter(type, val)(val); } private static readonly ConcurrentDictionary TypeDefaults = new ConcurrentDictionary(); private static readonly ConcurrentDictionary> TypeAsCasts = new ConcurrentDictionary>(); private static readonly ConcurrentDictionary> TypeConvert = new ConcurrentDictionary>(); private static readonly ParameterExpression ConvParameter = Expression.Parameter(typeof(object), "val"); [MethodImpl(MethodImplOptions.Synchronized)] private static Func GetConverter(Type targetType, object val) { Func fn; if (!targetType.IsValueType && !val.GetType().IsValueType) { if (!TypeAsCasts.TryGetValue(targetType, out fn)) { UnaryExpression instanceCast = Expression.TypeAs(ConvParameter, targetType); fn = Expression.Lambda>(Expression.TypeAs(instanceCast, typeof(object)), ConvParameter).Compile(); TypeAsCasts.AddOrUpdate(targetType, fn, (t, f) => fn); } } else { var fromType = val != null ? val.GetType() : typeof(object); var key = new PairOfTypes(fromType, targetType); if (TypeConvert.TryGetValue(key, out fn)) return fn; fn = (Func)Expression.Lambda(Expression.Convert(Expression.Convert(Expression.Convert(ConvParameter, fromType), targetType), typeof(object)), ConvParameter).Compile(); TypeConvert.AddOrUpdate(key, fn, (t, f) => fn); } return fn; } private class PairOfTypes { private readonly Type _first; private readonly Type _second; public PairOfTypes(Type first, Type second) { this._first = first; this._second = second; } public override int GetHashCode() { return (31 * _first.GetHashCode()) + _second.GetHashCode(); } public override bool Equals(object obj) { if (obj == this) return true; var other = obj as PairOfTypes; if (other == null) return false; return _first.Equals(other._first) && _second.Equals(other._second); } } } /// Class with mapper cache. public static class DynamicMapperCache { private static readonly object SyncLock = new object(); private static Dictionary _cache = new Dictionary(); /// Get type mapper. /// Type of mapper. /// Type mapper. public static DynamicTypeMap GetMapper() { return GetMapper(typeof(T)); } /// Get type mapper. /// Type of mapper. /// Type mapper. public static DynamicTypeMap GetMapper(Type type) { if (type == null) return null; /*if (type.IsAnonymous()) return null;*/ DynamicTypeMap mapper = null; lock (SyncLock) { if (!_cache.TryGetValue(type, out mapper)) { mapper = new DynamicTypeMap(type); if (mapper != null) _cache.Add(type, mapper); } } return mapper; } } /// Exception thrown when mapper fails to set or get a property. /// public class DynamicMapperException : Exception { /// Initializes a new instance of the class. public DynamicMapperException() { } /// Initializes a new instance of the class. /// The message that describes the error. public DynamicMapperException(string message) : base(message) { } /// Initializes a new instance of the class. /// The error message that explains the reason for the exception. /// The exception that is the cause of the current exception, or a null reference (Nothing in Visual Basic) if no inner exception is specified. public DynamicMapperException(string message, Exception innerException) : base(message, innerException) { } } /// Dynamic property invoker. public class DynamicPropertyInvoker { internal class ParameterSpec { public string Name { get; set; } public DbType Type { get; set; } public int Ordinal { get; set; } } /// Gets the array type of property if main type is a form of collection. public Type ArrayType { get; private set; } /// Gets a value indicating whether this property is in fact a generic list. public bool IsGnericEnumerable { get; private set; } /// Gets the type of property. public Type Type { get; private set; } /// Gets value getter. public Func Get { get; private set; } /// Gets value setter. public Action Setter { get; private set; } /// Gets the property information. public PropertyInfo PropertyInfo { get; private set; } /// Gets name of property. public string Name { get; private set; } /// Gets type column description. public ColumnAttribute Column { get; private set; } /// Gets type list of property requirements. public List Requirements { get; private set; } /// Gets a value indicating whether this is ignored in some cases. public bool Ignore { get; private set; } /// Gets a value indicating whether this instance hold data contract type. public bool IsDataContract { get; private set; } /// Initializes a new instance of the class. /// Property info to be invoked in the future. /// Column attribute if exist. public DynamicPropertyInvoker(PropertyInfo property, ColumnAttribute attr) { PropertyInfo = property; Name = property.Name; Type = property.PropertyType; object[] ignore = property.GetCustomAttributes(typeof(IgnoreAttribute), false); Requirements = property.GetCustomAttributes(typeof(RequiredAttribute), false).Cast().ToList(); Ignore = ignore != null && ignore.Length > 0; IsGnericEnumerable = Type.IsGenericEnumerable(); ArrayType = Type.IsArray ? Type.GetElementType() : IsGnericEnumerable ? Type.GetGenericArguments().First() : Type; IsDataContract = ArrayType.GetCustomAttributes(false).Any(x => x.GetType().Name == "DataContractAttribute"); if (ArrayType.IsArray) throw new InvalidOperationException("Jagged arrays are not supported"); if (ArrayType.IsGenericEnumerable()) throw new InvalidOperationException("Enumerables of enumerables are not supported"); Column = attr; if (attr != null && attr.AllowNull && !Type.IsNullableType() && Type != typeof(string)) attr.AllowNull = false; if (property.CanRead) Get = CreateGetter(property); if (property.CanWrite) Setter = CreateSetter(property); } private Func CreateGetter(PropertyInfo property) { if (property == null || !property.CanRead) return null; var objParm = Expression.Parameter(typeof(object), "o"); var target = property.DeclaringType.IsValueType ? (Expression)Expression.Convert(objParm, property.DeclaringType) : Expression.TypeAs(objParm, property.DeclaringType); return Expression.Lambda>( Expression.Convert( Expression.Property( target, property.Name), typeof(object)), objParm).Compile(); } private Action CreateSetter(PropertyInfo property) { if (!property.CanWrite) return null; var objParm = Expression.Parameter(typeof(object), "o"); var valueParm = Expression.Parameter(typeof(object), "value"); return Expression.Lambda>( Expression.Assign( Expression.Property( Expression.Convert(objParm, property.DeclaringType), property.Name), Expression.Convert(valueParm, property.PropertyType)), objParm, valueParm).Compile(); } /// Sets the specified value to destination object. /// The destination object. /// The value. public void Set(object dest, object val, bool byProperty = false) { object value = null; try { if (!Type.IsAssignableFrom(val.GetType())) { if (Type.IsArray || IsGnericEnumerable) { if (val != null) { if (val is IEnumerable) { var lst = (val as IEnumerable).Select(x => GetElementVal(ArrayType, x, byProperty)).ToList(); value = Array.CreateInstance(ArrayType, lst.Count); int i = 0; foreach (var e in lst) ((Array)value).SetValue(e, i++); } else { value = Array.CreateInstance(ArrayType, 1); ((Array)value).SetValue(GetElementVal(ArrayType, val, byProperty), 0); } } else value = Array.CreateInstance(ArrayType, 0); } else value = GetElementVal(Type, val, byProperty); } else value = val; Setter(dest, value); } catch (Exception ex) { throw new DynamicMapperException( string.Format("Error trying to convert and set value '{0}' of type '{1}' to type '{2}' in object of type '{3}'", val == null ? string.Empty : val.ToString(), val.GetType(), Type.FullName, dest.GetType().FullName), ex); } } private object GetElementVal(System.Type etype, object val, bool byProperty) { bool nullable = etype.IsGenericType && etype.GetGenericTypeDefinition() == typeof(Nullable<>); Type type = Nullable.GetUnderlyingType(etype) ?? etype; if (val == null && type.IsValueType) { if (nullable) return null; else return Activator.CreateInstance(Type); } else if ((val == null && !type.IsValueType) || (val != null && type == val.GetType())) return val; else if (type.IsEnum && val.GetType().IsValueType) return Enum.ToObject(type, val); else if (type.IsEnum) try { return Enum.Parse(type, val.ToString()); } catch (ArgumentException) { if (nullable) return null; throw; } else if (Type == typeof(string) && val.GetType() == typeof(Guid)) return val.ToString(); else if (Type == typeof(Guid)) { if (val.GetType() == typeof(byte[])) return new Guid((byte[])val); else if (val.GetType() == typeof(string)) { Guid g; return Guid.TryParse((string)val, out g) ? g : Guid.Empty; } else return (nullable) ? null : (object)Guid.Empty; } else if (!typeof(IConvertible).IsAssignableFrom(type) && (IsDataContract || (!type.IsValueType && val is IDictionary))) { if (byProperty) return val.MapByProperty(type); return val.Map(type); } else try { return Convert.ChangeType(val, type); } catch { if (nullable) return null; throw; } } #region Type command cache internal ParameterSpec InsertCommandParameter { get; set; } internal ParameterSpec UpdateCommandParameter { get; set; } internal ParameterSpec DeleteCommandParameter { get; set; } #endregion Type command cache } /// Represents type columnMap. public class DynamicTypeMap { /// Gets mapper destination type creator. public Type Type { get; private set; } /// Gets type table description. public TableAttribute Table { get; private set; } /// Gets object creator. public Func Creator { get; private set; } /// Gets map of columns to properties. /// Key: Column name (lower), Value: . public Dictionary ColumnsMap { get; private set; } /// Gets map of properties to column. /// Key: Property name, Value: Column name. public Dictionary PropertyMap { get; private set; } /// Gets list of ignored properties. public List Ignored { get; private set; } /// Initializes a new instance of the class. /// Type to which columnMap objects. public DynamicTypeMap(Type type) { Type = type; object[] attr = type.GetCustomAttributes(typeof(TableAttribute), false); if (attr != null && attr.Length > 0) Table = (TableAttribute)attr[0]; Creator = CreateCreator(); CreateColumnAndPropertyMap(); } private void CreateColumnAndPropertyMap() { Dictionary columnMap = new Dictionary(); Dictionary propertyMap = new Dictionary(); List ignored = new List(); foreach (PropertyInfo pi in GetAllMembers(Type).Where(x => x is PropertyInfo).Cast()) { // Skip indexers if (pi.GetIndexParameters().Any()) continue; ColumnAttribute attr = null; object[] attrs = pi.GetCustomAttributes(typeof(ColumnAttribute), true); if (attrs != null && attrs.Length > 0) attr = (ColumnAttribute)attrs[0]; string col = attr == null || string.IsNullOrEmpty(attr.Name) ? pi.Name : attr.Name; DynamicPropertyInvoker val = new DynamicPropertyInvoker(pi, attr); columnMap.Add(col.ToLower(), val); propertyMap.Add(pi.Name, col); if (val.Ignore) ignored.Add(pi.Name); } ColumnsMap = columnMap; PropertyMap = propertyMap; Ignored = ignored; ////columnMap.Where(i => i.Value.Ignore).Select(i => i.Value.Name).ToList(); } private Func CreateCreator() { var c = Type.GetConstructor(Type.EmptyTypes); if (c == null) c = Type.GetConstructor(BindingFlags.Instance | BindingFlags.NonPublic, null, Type.EmptyTypes, null); if (c != null) return Expression.Lambda>(Expression.New(Type)).Compile(); return null; } /// Create object of type and fill values from source. /// Object containing values that will be mapped to newly created object. /// New object of type with matching values from source. public object Create(object source) { return Map(source, Creator()); } /// Create object of type and fill values from source using property names. /// Object containing values that will be mapped to newly created object. /// New object of type with matching values from source. public object CreateByProperty(object source) { return MapByProperty(source, Creator()); } /// Fill values from source to object in destination. /// Object containing values that will be mapped to newly created object. /// Object of type to which copy values from source. /// Object of type with matching values from source. public object Map(object source, object destination) { DynamicPropertyInvoker dpi = null; foreach (KeyValuePair item in source.ToDictionary()) { if (ColumnsMap.TryGetValue(item.Key.ToLower(), out dpi) && item.Value != null) if (dpi.Setter != null) dpi.Set(destination, item.Value); } return destination; } /// Fill values from source to object in destination using property names. /// Object containing values that will be mapped to newly created object. /// Object of type to which copy values from source. /// Object of type with matching values from source. public object MapByProperty(object source, object destination) { string cn = null; DynamicPropertyInvoker dpi = null; foreach (KeyValuePair item in source.ToDictionary()) { if (PropertyMap.TryGetValue(item.Key, out cn) && item.Value != null) if (ColumnsMap.TryGetValue(cn.ToLower(), out dpi) && item.Value != null) if (dpi.Setter != null) dpi.Set(destination, item.Value, true); } return destination; } /// Validates the object. /// The value. /// List of not valid results. public IList ValidateObject(object val) { var result = new List(); if (val == null || val.GetType() != Type) return null; foreach (var prop in ColumnsMap.Values) { if (prop.Requirements == null || !prop.Requirements.Any()) continue; var v = prop.Get(val); foreach (var r in prop.Requirements.Where(x => !x.ElementRequirement)) { var valid = r.ValidateSimpleValue(prop, v); if (valid == ValidateResult.Valid) { if (prop.Type.IsArray || prop.IsGnericEnumerable) { var map = DynamicMapperCache.GetMapper(prop.ArrayType); var list = v as IEnumerable; if (list == null) { var enumerable = v as IEnumerable; if (enumerable != null) list = enumerable.Cast(); } if (list != null) foreach (var item in list) { if (prop.Requirements.Any(x => x.ElementRequirement)) { foreach (var re in prop.Requirements.Where(x => x.ElementRequirement)) { var validelem = re.ValidateSimpleValue(prop.ArrayType, prop.ArrayType.IsGenericEnumerable(), item); if (validelem == ValidateResult.NotSupported) { result.AddRange(map.ValidateObject(item)); break; } else if (validelem != ValidateResult.Valid) result.Add(new ValidationResult() { Property = prop, Requirement = r, Value = item, Result = validelem, }); } } else result.AddRange(map.ValidateObject(item)); } } continue; } if (valid == ValidateResult.NotSupported) { result.AddRange(DynamicMapperCache.GetMapper(prop.Type).ValidateObject(v)); continue; } result.Add(new ValidationResult() { Property = prop, Requirement = r, Value = v, Result = valid, }); } } return result; } private IEnumerable GetAllMembers(Type type) { if (type.IsInterface) { List members = new List(); List considered = new List(); Queue queue = new Queue(); considered.Add(type); queue.Enqueue(type); while (queue.Count > 0) { Type subType = queue.Dequeue(); foreach (Type subInterface in subType.GetInterfaces()) { if (considered.Contains(subInterface)) continue; considered.Add(subInterface); queue.Enqueue(subInterface); } MemberInfo[] typeProperties = subType.GetMembers( BindingFlags.FlattenHierarchy | BindingFlags.Public | BindingFlags.Instance); IEnumerable newPropertyInfos = typeProperties .Where(x => !members.Contains(x)); members.InsertRange(0, newPropertyInfos); } return members; } return type.GetMembers(BindingFlags.FlattenHierarchy | BindingFlags.Public | BindingFlags.Instance); } #region Type command cache internal string InsertCommandText { get; set; } internal string UpdateCommandText { get; set; } internal string DeleteCommandText { get; set; } #endregion Type command cache } /// Allows to add ignore action to property. /// Property still get's mapped from output. [AttributeUsage(AttributeTargets.Property)] public class IgnoreAttribute : Attribute { } /// Declares metadata for object-based stored procedure parameters. [AttributeUsage(AttributeTargets.Property)] public class ProcedureParameterAttribute : ColumnAttribute { /// Sentinel used when database type was not provided. public const int UnspecifiedDbType = -1; /// Sentinel used when size was not provided. public const int UnspecifiedSize = -1; /// Sentinel used when precision or scale was not provided. public const byte UnspecifiedByte = byte.MaxValue; /// Gets or sets parameter direction. Defaults to input. public ParameterDirection Direction { get; set; } /// Gets or sets explicit parameter order. Lower values are emitted first. public int Order { get; set; } /// Gets or sets parameter database type. public DbType DbType { get; set; } /// Gets or sets parameter size. public new int Size { get; set; } /// Gets or sets parameter precision. public new byte Precision { get; set; } /// Gets or sets parameter scale. public new byte Scale { get; set; } /// Initializes a new instance of the class. public ProcedureParameterAttribute() { Direction = ParameterDirection.Input; Order = int.MaxValue; DbType = (DbType)UnspecifiedDbType; Size = UnspecifiedSize; Precision = UnspecifiedByte; Scale = UnspecifiedByte; } /// Initializes a new instance of the class. public ProcedureParameterAttribute(string name) : this() { Name = name; } } /// Declares mapping of a typed procedure result property to a specific result set. [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)] public class ProcedureResultAttribute : Attribute { /// Main procedure result marker. public const int MainResultIndex = -1; /// Initializes a new instance of the class. public ProcedureResultAttribute() : this(MainResultIndex) { } /// Initializes a new instance of the class. public ProcedureResultAttribute(int resultIndex) { ResultIndex = resultIndex; } /// Gets result-set index in reader order, zero based. public int ResultIndex { get; private set; } /// Gets or sets optional column name for scalar/simple list extraction. public string ColumnName { get; set; } /// Gets or sets optional name used for DataTable. public string Name { get; set; } } /// Allows to add table name to class. [AttributeUsage(AttributeTargets.Class)] public class TableAttribute : Attribute { /// Gets or sets table owner name. public string Owner { get; set; } /// Gets or sets name. public string Name { get; set; } /// Gets or sets a value indicating whether override database /// schema values. /// If database doesn't support schema, you still have to /// set this to true to get schema from type. public bool Override { get; set; } } } namespace Objects { /// Base class for strong typed objects. public class DynamicEntityBase { private Dictionary _changedFields = new Dictionary(); private DynamicEntityState _dynamicEntityState = DynamicEntityState.Unknown; /// Occurs when object property is changing. public event EventHandler PropertyChanging; /// Gets the state of the dynamic entity. /// Current state of entity. public virtual DynamicEntityState GetDynamicEntityState() { return _dynamicEntityState; } /// Sets the state of the dynamic entity. /// Using this method will reset modified fields list. /// The state. public virtual void SetDynamicEntityState(DynamicEntityState state) { _dynamicEntityState = state; if (_changedFields != null) _changedFields.Clear(); } /// Called when object property is changing. /// Name of the property. /// The old property value. /// The new property value. protected virtual void OnPropertyChanging(string propertyName, object oldValue, object newValue) { OnPropertyChanging(new DynamicPropertyChangingEventArgs(propertyName, oldValue, newValue)); } /// Raises the event. /// The instance containing the event data. protected virtual void OnPropertyChanging(DynamicPropertyChangingEventArgs e) { _changedFields[e.PropertyName] = e.NewValue; if (PropertyChanging != null) PropertyChanging(this, e); } /// Validates this object instance. /// Returns list of containing results of validation. public virtual IList Validate() { return DynamicMapperCache.GetMapper(this.GetType()).ValidateObject(this); } /// Saves this object to database. /// The database. /// Returns true if operation was successful. /// Can be thrown when object is in invalid state. public virtual bool Save(DynamicDatabase database) { switch (GetDynamicEntityState()) { default: case DynamicEntityState.Unknown: throw new InvalidOperationException("Unknown object state. Unable to decide whish action should be performed."); case DynamicEntityState.New: return Insert(database); case DynamicEntityState.Existing: if (IsModified()) return Update(database); return true; case DynamicEntityState.ToBeDeleted: return Delete(database); case DynamicEntityState.Deleted: throw new InvalidOperationException("Unable to do any database action on deleted object."); } } /// Determines whether this instance is in existing state and fields was modified since this state was set modified. /// Returns true if this instance is modified; otherwise, false. public virtual bool IsModified() { if (GetDynamicEntityState() != DynamicEntityState.Existing) return false; return _changedFields != null && _changedFields.Any(); } #region Insert/Update/Delete /// Inserts this object to database. /// The database. /// Returns true if operation was successful. public virtual bool Insert(DynamicDatabase db) { using (var query = db.Insert(this.GetType())) { if (query .Values(x => this) .Execute() > 0) { _changedFields.Clear(); SetDynamicEntityState(DynamicEntityState.Existing); return true; } } return false; } /// Updates this object in database. /// The database. /// Returns true if operation was successful. public virtual bool Update(DynamicDatabase db) { var t = GetType(); var mapper = DynamicMapperCache.GetMapper(t); using (var query = db.Update(t)) { MakeQueryWhere(mapper, query); bool any = false; if (_changedFields.Any()) { foreach (var cf in _changedFields) { var cn = mapper.PropertyMap[cf.Key]; var pm = mapper.ColumnsMap[cn.ToLower()]; if (pm.Ignore) continue; if (pm.Column != null) { if (pm.Column.IsKey || pm.Column.IsNoUpdate) continue; if (!pm.Column.AllowNull && cf.Value == null) continue; } query.Values(cn, cf.Value); any = true; } } if (!any) foreach (var pmk in mapper.ColumnsMap) { var pm = pmk.Value; var val = pm.Get(this); var cn = pm.Name; if (pm.Ignore) continue; if (pm.Column != null) { if (!string.IsNullOrEmpty(pm.Column.Name)) cn = pm.Column.Name; if (pm.Column.IsKey) continue; if (!pm.Column.AllowNull && val == null) continue; } query.Values(cn, val); } if (query.Execute() == 0) return false; SetDynamicEntityState(DynamicEntityState.Existing); _changedFields.Clear(); return true; } } /// Deletes this object from database. /// The database. /// Returns true if operation was successful. public virtual bool Delete(DynamicDatabase db) { var t = this.GetType(); var mapper = DynamicMapperCache.GetMapper(t); using (var query = db.Delete(t)) { MakeQueryWhere(mapper, query); if (query.Execute() == 0) return false; SetDynamicEntityState(DynamicEntityState.Deleted); } return true; } #endregion Insert/Update/Delete #region Select /// Refresh non key data from database. /// The database. /// All properties that are primary key values must be filled. /// Returns true if operation was successful. public virtual bool Refresh(DynamicDatabase db) { var t = this.GetType(); var mapper = DynamicMapperCache.GetMapper(t); using (var query = db.From(t)) { MakeQueryWhere(mapper, query); var o = (query.Execute() as IEnumerable).FirstOrDefault(); if (o == null) return false; mapper.Map(o, this); SetDynamicEntityState(DynamicEntityState.Existing); _changedFields.Clear(); } return true; } #endregion Select #region Query Helpers private void MakeQueryWhere(DynamicTypeMap mapper, IDynamicUpdateQueryBuilder query) { bool keyNotDefined = true; foreach (var cm in mapper.ColumnsMap) { if (cm.Value.Column != null && cm.Value.Column.IsKey) { query.Where(cm.Key, DynamicColumn.CompareOperator.Eq, cm.Value.Get(this)); keyNotDefined = false; } } if (keyNotDefined) throw new InvalidOperationException(String.Format("Class '{0}' have no key columns defined", this.GetType().FullName)); } private void MakeQueryWhere(DynamicTypeMap mapper, IDynamicDeleteQueryBuilder query) { bool keyNotDefined = true; foreach (var cm in mapper.ColumnsMap) { if (cm.Value.Column != null && cm.Value.Column.IsKey) { query.Where(cm.Key, DynamicColumn.CompareOperator.Eq, cm.Value.Get(this)); keyNotDefined = false; } } if (keyNotDefined) throw new InvalidOperationException(String.Format("Class '{0}' have no key columns defined", this.GetType().FullName)); } private void MakeQueryWhere(DynamicTypeMap mapper, IDynamicSelectQueryBuilder query) { bool keyNotDefined = true; foreach (var cm in mapper.ColumnsMap) { if (cm.Value.Column != null && cm.Value.Column.IsKey) { var v = cm.Value.Get(this); if (v == null) throw new InvalidOperationException(String.Format("Class '{0}' have key columns {1} not filled with data.", this.GetType().FullName, cm.Value.Name)); query.Where(cm.Key, DynamicColumn.CompareOperator.Eq, cm.Value.Get(this)); keyNotDefined = false; } } if (keyNotDefined) throw new InvalidOperationException(String.Format("Class '{0}' have no key columns defined", this.GetType().FullName)); } #endregion Query Helpers } /// Possible states of dynamic database objects. public enum DynamicEntityState { /// Default state. This state will only permit to refresh data from database. /// In this state repository will be unable to tell if object with this state should be added /// or updated in database, but you can still manually perform update or insert on such object. Unknown, /// This state should be set to new objects in database. New, /// This state is ser when data is refreshed from database or object was loaded from repository. Existing, /// You can set this state to an object if you want repository to perform delete from database. ToBeDeleted, /// This state is set for objects that were deleted from database. Deleted, } /// Class containing changed property data. /// public class DynamicPropertyChangingEventArgs : EventArgs { /// Gets the name of the property. /// The name of the property. public string PropertyName { get; private set; } /// Gets the old property value. /// The old value. public object OldValue { get; private set; } /// Gets the new property value. /// The new value. public object NewValue { get; private set; } /// Initializes a new instance of the class. /// Name of the property. /// The old property value. /// The new property value. public DynamicPropertyChangingEventArgs(string propertyName, object oldValue, object newValue) { PropertyName = propertyName; OldValue = oldValue; NewValue = newValue; } } /// Base repository class for specified object type. /// Type of stored object. public class DynamicRepositoryBase : IDisposable where T : DynamicEntityBase { private DynamicDatabase _database; /// Initializes a new instance of the class. /// The database. public DynamicRepositoryBase(DynamicDatabase database) { _database = database; } /// Get all rows from database. /// Objects enumerator. public virtual IEnumerable GetAll() { using (var q = _database.From()) return EnumerateQuery(q); } /// Get rows from database by custom query. /// The query. /// Query must be based on object type. /// Objects enumerator. public virtual IEnumerable GetByQuery(IDynamicSelectQueryBuilder query) { return EnumerateQuery(query); } private IEnumerable EnumerateQuery(IDynamicSelectQueryBuilder query, bool forceType = true) { if (forceType) { var mapper = DynamicMapperCache.GetMapper(typeof(T)); var tn = mapper.Table == null || string.IsNullOrEmpty(mapper.Table.Name) ? mapper.Type.Name : mapper.Table.Name; if (!query.Tables.Any(t => t.Name == tn)) throw new InvalidOperationException(string.Format("Query is not related to '{0}' class.", typeof(T).FullName)); } foreach (var o in query.Execute()) { o.SetDynamicEntityState(DynamicEntityState.Existing); yield return o; } } /// Saves single object to database. /// The element. /// Returns true if operation was successful. public virtual bool Save(T element) { return element.Save(_database); } /// Saves collection of objects to database. /// The element. /// Returns true if operation was successful. public virtual bool Save(IEnumerable element) { return element.All(x => x.Save(_database)); } /// Insert single object to database. /// The element. /// Returns true if operation was successful. public virtual bool Insert(T element) { return element.Insert(_database); } /// Insert collection of objects to database. /// The element. /// Returns true if operation was successful. public virtual bool Insert(IEnumerable element) { return element.All(x => x.Insert(_database)); } /// Update single object to database. /// The element. /// Returns true if operation was successful. public virtual bool Update(T element) { return element.Update(_database); } /// Update collection of objects to database. /// The element. /// Returns true if operation was successful. public virtual bool Update(IEnumerable element) { return element.All(x => x.Update(_database)); } /// Delete single object to database. /// The element. /// Returns true if operation was successful. public virtual bool Delete(T element) { return element.Delete(_database); } /// Delete collection of objects to database. /// The element. /// Returns true if operation was successful. public virtual bool Delete(IEnumerable element) { return element.All(x => x.Delete(_database)); } /// Releases unmanaged and - optionally - managed resources. public virtual void Dispose() { _database = null; } } /// Marks an object as an explicit stored procedure parameter contract. public interface IProcedureParameters { } /// Marks an object as a stored procedure parameter contract with a declared typed result model. /// Typed result model. public interface IProcedureParameters : IProcedureParameters { } /// Allows typed procedure result models to consume multiple result sets directly. public interface IProcedureResultReader { /// Reads all required result sets from the procedure reader. /// Procedure result reader, usually a cached reader. void ReadResults(IDataReader reader); } } namespace TypedSql { /// Render context used by typed SQL DSL nodes. public interface ITypedSqlRenderContext { /// Resolve mapped column for given model member. string ResolveColumn(Type modelType, string memberName, string alias); /// Render value as SQL parameter or literal fragment. string RenderValue(object value); /// Decorate SQL identifier. string DecorateName(string name); /// Render subquery SQL and merge any parameters into current context. string RenderSubQuery(Builders.IDynamicSelectQueryBuilder query); } /// Entry point for the typed SQL DSL. public static class Sql { /// Create parameterized value expression. public static TypedSqlExpression Val(T value) { return new TypedSqlValueExpression(value); } /// Create parameterized value expression. public static TypedSqlExpression Val(object value) { return new TypedSqlValueExpression(value); } /// Create typed table context for an explicitly named alias, including joined aliases. public static TypedTableContext Table(string alias) { return new TypedTableContext(alias); } /// Create raw SQL expression. public static TypedSqlExpression Raw(string sql) { return new TypedSqlRawExpression(sql); } /// Create raw ORDER BY fragment. public static TypedSqlOrderExpression RawOrder(string sql) { return new TypedSqlRawOrderExpression(Raw(sql)); } /// Create generic function call. public static TypedSqlExpression Func(string name, params TypedSqlExpression[] arguments) { return new TypedSqlFunctionExpression(name, arguments); } /// Create COUNT(*) expression. public static TypedSqlExpression Count() { return Raw("COUNT(*)"); } /// Create COUNT(expr) expression. public static TypedSqlExpression Count(TypedSqlExpression expression) { return Func("COUNT", expression); } /// Create COALESCE expression. public static TypedSqlExpression Coalesce(params TypedSqlExpression[] expressions) { return Func("COALESCE", expressions); } /// Create SUM expression. public static TypedSqlExpression Sum(TypedSqlExpression expression) { return Func("SUM", expression); } /// Create AVG expression. public static TypedSqlExpression Avg(TypedSqlExpression expression) { return Func("AVG", expression); } /// Create MIN expression. public static TypedSqlExpression Min(TypedSqlExpression expression) { return Func("MIN", expression); } /// Create MAX expression. public static TypedSqlExpression Max(TypedSqlExpression expression) { return Func("MAX", expression); } /// Create ABS expression. public static TypedSqlExpression Abs(TypedSqlExpression expression) { return Func("ABS", expression); } /// Create UPPER expression. public static TypedSqlExpression Upper(TypedSqlExpression expression) { return Func("UPPER", expression); } /// Create LOWER expression. public static TypedSqlExpression Lower(TypedSqlExpression expression) { return Func("LOWER", expression); } /// Create TRIM expression. public static TypedSqlExpression Trim(TypedSqlExpression expression) { return Func("TRIM", expression); } /// Create LENGTH expression. public static TypedSqlExpression Length(TypedSqlExpression expression) { return Func("LENGTH", expression); } /// Create NULLIF expression. public static TypedSqlExpression NullIf(TypedSqlExpression left, TypedSqlExpression right) { return Func("NULLIF", left, right); } /// Create CURRENT_TIMESTAMP expression. public static TypedSqlExpression CurrentTimestamp() { return Raw("CURRENT_TIMESTAMP"); } /// Create scalar subquery expression. public static TypedSqlExpression SubQuery(IDynamicSelectQueryBuilder query) { return new TypedSqlSubQueryExpression(query); } /// Create scalar typed subquery expression without manually constructing the builder. public static TypedSqlExpression SubQuery(DynamicDatabase db, Func, IDynamicSelectQueryBuilder> configure, string alias = null, bool noLock = false) { if (db == null) throw new ArgumentNullException("db"); if (configure == null) throw new ArgumentNullException("configure"); return new TypedSqlSubQueryExpression(configure(db.FromTyped(alias, noLock))); } /// Create scalar typed subquery expression using the scoped typed builder API. public static TypedSqlExpression SubQueryScope(DynamicDatabase db, Func, IDynamicSelectQueryBuilder> configure, string alias = null, bool noLock = false) { if (db == null) throw new ArgumentNullException("db"); if (configure == null) throw new ArgumentNullException("configure"); return new TypedSqlSubQueryExpression(configure(db.FromTypedScope(alias, noLock))); } /// Create EXISTS predicate. public static TypedSqlPredicate Exists(IDynamicSelectQueryBuilder query) { return new TypedSqlExistsPredicate(query); } /// Create EXISTS predicate from typed subquery factory. public static TypedSqlPredicate Exists(DynamicDatabase db, Func, IDynamicSelectQueryBuilder> configure, string alias = null, bool noLock = false) { if (db == null) throw new ArgumentNullException("db"); if (configure == null) throw new ArgumentNullException("configure"); return new TypedSqlExistsPredicate(configure(db.FromTyped(alias, noLock))); } /// Create EXISTS predicate from scoped typed subquery factory. public static TypedSqlPredicate ExistsScope(DynamicDatabase db, Func, IDynamicSelectQueryBuilder> configure, string alias = null, bool noLock = false) { if (db == null) throw new ArgumentNullException("db"); if (configure == null) throw new ArgumentNullException("configure"); return new TypedSqlExistsPredicate(configure(db.FromTypedScope(alias, noLock))); } /// Create CASE expression builder. public static TypedSqlCaseBuilder Case() { return new TypedSqlCaseBuilder(); } } /// Builder for CASE expressions. /// Result type. public sealed class TypedSqlCaseBuilder { private readonly IList> _cases = new List>(); /// Add WHEN ... THEN ... clause. public TypedSqlCaseBuilder When(TypedSqlPredicate predicate, object value) { _cases.Add(new KeyValuePair( predicate, value as TypedSqlExpression ?? Sql.Val(value))); return this; } /// Finalize CASE expression with ELSE clause. public TypedSqlExpression Else(object value) { return new TypedSqlCaseExpression(_cases, value as TypedSqlExpression ?? Sql.Val(value)); } /// Finalize CASE expression without ELSE clause. public TypedSqlExpression End() { return new TypedSqlCaseExpression(_cases, null); } } /// Base selectable SQL fragment for the typed DSL. public abstract class TypedSqlSelectable { internal abstract string Render(ITypedSqlRenderContext context); } /// Base SQL expression for the typed DSL. public abstract class TypedSqlExpression : TypedSqlSelectable { /// Add arithmetic expression. public static TypedSqlExpression operator +(TypedSqlExpression left, TypedSqlExpression right) { return new TypedSqlBinaryExpression(left, "+", right); } /// Subtract arithmetic expression. public static TypedSqlExpression operator -(TypedSqlExpression left, TypedSqlExpression right) { return new TypedSqlBinaryExpression(left, "-", right); } /// Multiply arithmetic expression. public static TypedSqlExpression operator *(TypedSqlExpression left, TypedSqlExpression right) { return new TypedSqlBinaryExpression(left, "*", right); } /// Divide arithmetic expression. public static TypedSqlExpression operator /(TypedSqlExpression left, TypedSqlExpression right) { return new TypedSqlBinaryExpression(left, "/", right); } /// Modulo arithmetic expression. public static TypedSqlExpression operator %(TypedSqlExpression left, TypedSqlExpression right) { return new TypedSqlBinaryExpression(left, "%", right); } /// Alias this expression in SELECT clause. public TypedSqlAliasedExpression As(string alias) { return new TypedSqlAliasedExpression(this, alias); } /// Order ascending. public TypedSqlOrderExpression Asc() { return new TypedSqlOrderExpression(this, true); } /// Order descending. public TypedSqlOrderExpression Desc() { return new TypedSqlOrderExpression(this, false); } /// Add expression to another value. public TypedSqlExpression Add(object value) { return new TypedSqlBinaryExpression(this, "+", value as TypedSqlExpression ?? Sql.Val(value)); } /// Subtract another value. public TypedSqlExpression Sub(object value) { return new TypedSqlBinaryExpression(this, "-", value as TypedSqlExpression ?? Sql.Val(value)); } /// Multiply by another value. public TypedSqlExpression Mul(object value) { return new TypedSqlBinaryExpression(this, "*", value as TypedSqlExpression ?? Sql.Val(value)); } /// Divide by another value. public TypedSqlExpression Div(object value) { return new TypedSqlBinaryExpression(this, "/", value as TypedSqlExpression ?? Sql.Val(value)); } /// Modulo by another value. public TypedSqlExpression Mod(object value) { return new TypedSqlBinaryExpression(this, "%", value as TypedSqlExpression ?? Sql.Val(value)); } /// Equality predicate. public TypedSqlPredicate Eq(object value) { return new TypedSqlBinaryPredicate(this, "=", value is TypedSqlExpression ? (TypedSqlExpression)value : Sql.Val(value)); } /// Inequality predicate. public TypedSqlPredicate NotEq(object value) { return new TypedSqlBinaryPredicate(this, "<>", value is TypedSqlExpression ? (TypedSqlExpression)value : Sql.Val(value)); } /// Greater-than predicate. public TypedSqlPredicate Gt(object value) { return new TypedSqlBinaryPredicate(this, ">", value is TypedSqlExpression ? (TypedSqlExpression)value : Sql.Val(value)); } /// Greater-than-or-equal predicate. public TypedSqlPredicate Gte(object value) { return new TypedSqlBinaryPredicate(this, ">=", value is TypedSqlExpression ? (TypedSqlExpression)value : Sql.Val(value)); } /// Less-than predicate. public TypedSqlPredicate Lt(object value) { return new TypedSqlBinaryPredicate(this, "<", value is TypedSqlExpression ? (TypedSqlExpression)value : Sql.Val(value)); } /// Less-than-or-equal predicate. public TypedSqlPredicate Lte(object value) { return new TypedSqlBinaryPredicate(this, "<=", value is TypedSqlExpression ? (TypedSqlExpression)value : Sql.Val(value)); } /// IS NULL predicate. public TypedSqlPredicate IsNull() { return new TypedSqlUnaryPredicate(this, "IS NULL"); } /// IS NOT NULL predicate. public TypedSqlPredicate IsNotNull() { return new TypedSqlUnaryPredicate(this, "IS NOT NULL"); } /// LIKE predicate. public TypedSqlPredicate Like(string pattern) { return new TypedSqlBinaryPredicate(this, "LIKE", Sql.Val(pattern)); } /// IN predicate. public TypedSqlPredicate In(params object[] values) { return new TypedSqlInPredicate(this, values); } /// IN predicate. public TypedSqlPredicate In(IEnumerable values) { return new TypedSqlInPredicate(this, values); } /// NOT IN predicate. public TypedSqlPredicate NotIn(params object[] values) { return new TypedSqlInPredicate(this, values, true); } /// NOT IN predicate. public TypedSqlPredicate NotIn(IEnumerable values) { return new TypedSqlInPredicate(this, values, true); } /// BETWEEN predicate. public TypedSqlPredicate Between(object lower, object upper) { return new TypedSqlBetweenPredicate(this, lower is TypedSqlExpression ? (TypedSqlExpression)lower : Sql.Val(lower), upper is TypedSqlExpression ? (TypedSqlExpression)upper : Sql.Val(upper)); } /// Starts-with LIKE predicate. public TypedSqlPredicate StartsWith(string value) { return Like((value ?? string.Empty) + "%"); } /// Ends-with LIKE predicate. public TypedSqlPredicate EndsWith(string value) { return Like("%" + (value ?? string.Empty)); } /// Contains LIKE predicate. public TypedSqlPredicate Contains(string value) { return Like("%" + (value ?? string.Empty) + "%"); } } /// Typed SQL expression. public abstract class TypedSqlExpression : TypedSqlExpression { } /// Typed SQL predicate expression. public abstract class TypedSqlPredicate : TypedSqlExpression { /// Combine with AND. public TypedSqlPredicate And(TypedSqlPredicate right) { return new TypedSqlCombinedPredicate(this, "AND", right); } /// Combine with OR. public TypedSqlPredicate Or(TypedSqlPredicate right) { return new TypedSqlCombinedPredicate(this, "OR", right); } /// Negate predicate. public TypedSqlPredicate Not() { return new TypedSqlNegatedPredicate(this); } } /// Aliased SQL expression. public sealed class TypedSqlAliasedExpression : TypedSqlSelectable { private readonly TypedSqlExpression _expression; private readonly string _alias; internal TypedSqlAliasedExpression(TypedSqlExpression expression, string alias) { _expression = expression; _alias = alias; } internal override string Render(ITypedSqlRenderContext context) { return string.Format("{0} AS {1}", _expression.Render(context), context.DecorateName(_alias)); } } /// Ordered SQL expression. public class TypedSqlOrderExpression : TypedSqlSelectable { private readonly TypedSqlExpression _expression; private readonly bool _ascending; private readonly string _nullOrdering; private readonly bool _raw; internal TypedSqlOrderExpression(TypedSqlExpression expression, bool ascending, string nullOrdering = null, bool raw = false) { _expression = expression; _ascending = ascending; _nullOrdering = nullOrdering; _raw = raw; } /// Append NULLS FIRST ordering. public TypedSqlOrderExpression NullsFirst() { return new TypedSqlOrderExpression(_expression, _ascending, "NULLS FIRST", _raw); } /// Append NULLS LAST ordering. public TypedSqlOrderExpression NullsLast() { return new TypedSqlOrderExpression(_expression, _ascending, "NULLS LAST", _raw); } internal override string Render(ITypedSqlRenderContext context) { string rendered = _raw ? _expression.Render(context) : string.Format("{0} {1}", _expression.Render(context), _ascending ? "ASC" : "DESC"); if (!string.IsNullOrEmpty(_nullOrdering)) rendered = string.Format("{0} {1}", rendered, _nullOrdering); return rendered; } } internal sealed class TypedSqlRawOrderExpression : TypedSqlOrderExpression { internal TypedSqlRawOrderExpression(TypedSqlExpression expression) : base(expression, true, null, true) { } } internal sealed class TypedSqlBinaryExpression : TypedSqlExpression { private readonly TypedSqlExpression _left; private readonly string _operator; private readonly TypedSqlExpression _right; internal TypedSqlBinaryExpression(TypedSqlExpression left, string op, TypedSqlExpression right) { _left = left; _operator = op; _right = right; } internal override string Render(ITypedSqlRenderContext context) { return string.Format("({0} {1} {2})", _left.Render(context), _operator, _right.Render(context)); } } internal sealed class TypedSqlColumnExpression : TypedSqlExpression { private readonly Type _modelType; private readonly string _memberName; private readonly string _alias; internal TypedSqlColumnExpression(Type modelType, string memberName, string alias) { _modelType = modelType; _memberName = memberName; _alias = alias; } internal override string Render(ITypedSqlRenderContext context) { return context.ResolveColumn(_modelType, _memberName, _alias); } } internal sealed class TypedSqlValueExpression : TypedSqlExpression, ITypedSqlNullValue { private readonly object _value; internal TypedSqlValueExpression(object value) { _value = value; } internal override string Render(ITypedSqlRenderContext context) { return context.RenderValue(_value); } public bool IsNullValue { get { return _value == null; } } } internal sealed class TypedSqlRawExpression : TypedSqlExpression { private readonly string _sql; internal TypedSqlRawExpression(string sql) { _sql = sql; } internal override string Render(ITypedSqlRenderContext context) { return _sql; } } internal sealed class TypedSqlFunctionExpression : TypedSqlExpression { private readonly string _name; private readonly IList _arguments; internal TypedSqlFunctionExpression(string name, params TypedSqlExpression[] arguments) { _name = name; _arguments = arguments ?? new TypedSqlExpression[0]; } internal override string Render(ITypedSqlRenderContext context) { List rendered = new List(); foreach (TypedSqlExpression argument in _arguments) rendered.Add(argument.Render(context)); return string.Format("{0}({1})", _name, string.Join(", ", rendered.ToArray())); } } internal sealed class TypedSqlUnaryPredicate : TypedSqlPredicate { private readonly TypedSqlExpression _expression; private readonly string _operator; internal TypedSqlUnaryPredicate(TypedSqlExpression expression, string op) { _expression = expression; _operator = op; } internal override string Render(ITypedSqlRenderContext context) { return string.Format("({0} {1})", _expression.Render(context), _operator); } } internal sealed class TypedSqlBinaryPredicate : TypedSqlPredicate { private readonly TypedSqlExpression _left; private readonly string _operator; private readonly TypedSqlExpression _right; internal TypedSqlBinaryPredicate(TypedSqlExpression left, string op, TypedSqlExpression right) { _left = left; _operator = op; _right = right; } internal override string Render(ITypedSqlRenderContext context) { string op = _operator; TypedSqlValueExpression objRight = _right as TypedSqlValueExpression; if ((objRight != null && objRight.IsNullValue) || (_right is ITypedSqlNullValue && ((ITypedSqlNullValue)_right).IsNullValue)) op = _operator == "=" ? "IS" : _operator == "<>" ? "IS NOT" : _operator; return string.Format("({0} {1} {2})", _left.Render(context), op, _right.Render(context)); } } internal interface ITypedSqlNullValue { bool IsNullValue { get; } } internal sealed class TypedSqlInPredicate : TypedSqlPredicate { private readonly TypedSqlExpression _left; private readonly IEnumerable _values; private readonly bool _negated; internal TypedSqlInPredicate(TypedSqlExpression left, IEnumerable values, bool negated = false) { _left = left; _values = values; _negated = negated; } internal override string Render(ITypedSqlRenderContext context) { List rendered = new List(); foreach (object value in _values) rendered.Add((value as TypedSqlExpression ?? Sql.Val(value)).Render(context)); if (rendered.Count == 0) return _negated ? "(1 = 1)" : "(1 = 0)"; return string.Format("({0} {1}({2}))", _left.Render(context), _negated ? "NOT IN" : "IN", string.Join(", ", rendered.ToArray())); } } internal sealed class TypedSqlBetweenPredicate : TypedSqlPredicate { private readonly TypedSqlExpression _left; private readonly TypedSqlExpression _lower; private readonly TypedSqlExpression _upper; internal TypedSqlBetweenPredicate(TypedSqlExpression left, TypedSqlExpression lower, TypedSqlExpression upper) { _left = left; _lower = lower; _upper = upper; } internal override string Render(ITypedSqlRenderContext context) { return string.Format("({0} BETWEEN {1} AND {2})", _left.Render(context), _lower.Render(context), _upper.Render(context)); } } internal sealed class TypedSqlCombinedPredicate : TypedSqlPredicate { private readonly TypedSqlPredicate _left; private readonly string _operator; private readonly TypedSqlPredicate _right; internal TypedSqlCombinedPredicate(TypedSqlPredicate left, string op, TypedSqlPredicate right) { _left = left; _operator = op; _right = right; } internal override string Render(ITypedSqlRenderContext context) { return string.Format("({0} {1} {2})", _left.Render(context), _operator, _right.Render(context)); } } internal sealed class TypedSqlNegatedPredicate : TypedSqlPredicate { private readonly TypedSqlPredicate _predicate; internal TypedSqlNegatedPredicate(TypedSqlPredicate predicate) { _predicate = predicate; } internal override string Render(ITypedSqlRenderContext context) { return string.Format("(NOT {0})", _predicate.Render(context)); } } internal sealed class TypedSqlCaseExpression : TypedSqlExpression { private readonly IList> _cases; private readonly TypedSqlExpression _elseExpression; internal TypedSqlCaseExpression(IList> cases, TypedSqlExpression elseExpression) { _cases = cases; _elseExpression = elseExpression; } internal override string Render(ITypedSqlRenderContext context) { List items = new List(); items.Add("CASE"); foreach (KeyValuePair item in _cases) items.Add(string.Format("WHEN {0} THEN {1}", item.Key.Render(context), item.Value.Render(context))); if (_elseExpression != null) items.Add(string.Format("ELSE {0}", _elseExpression.Render(context))); items.Add("END"); return string.Join(" ", items.ToArray()); } } internal sealed class TypedSqlSubQueryExpression : TypedSqlExpression { private readonly IDynamicSelectQueryBuilder _query; internal TypedSqlSubQueryExpression(IDynamicSelectQueryBuilder query) { _query = query; } internal override string Render(ITypedSqlRenderContext context) { return string.Format("({0})", context.RenderSubQuery(_query)); } } internal sealed class TypedSqlExistsPredicate : TypedSqlPredicate { private readonly IDynamicSelectQueryBuilder _query; internal TypedSqlExistsPredicate(IDynamicSelectQueryBuilder query) { _query = query; } internal override string Render(ITypedSqlRenderContext context) { return string.Format("(EXISTS ({0}))", context.RenderSubQuery(_query)); } } /// Typed table context used by the typed SQL DSL. /// Mapped entity type. public sealed class TypedTableContext { internal TypedTableContext(string alias) { Alias = alias; } /// Gets table alias used by the current query. public string Alias { get; private set; } /// Creates a mapped column expression. public TypedSqlExpression Col(Expression> selector) { if (selector == null) throw new ArgumentNullException("selector"); MemberExpression member = selector.Body as MemberExpression; if (member == null && selector.Body is UnaryExpression) member = ((UnaryExpression)selector.Body).Operand as MemberExpression; if (member == null) throw new NotSupportedException(string.Format("Column selector must target a mapped property: {0}", selector)); return new TypedSqlColumnExpression(typeof(T), member.Member.Name, Alias); } /// Select all columns from this typed table context. public TypedSqlExpression All() { return Sql.Raw(string.IsNullOrEmpty(Alias) ? "*" : Alias + ".*"); } } } namespace Validation { /// Required attribute can be used to validate fields in objects using mapper class. [AttributeUsage(AttributeTargets.Property, AllowMultiple = true)] public class RequiredAttribute : Attribute { /// Gets or sets minimum value or length of field. public decimal? Min { get; set; } /// Gets or sets maximum value or length of field. public decimal? Max { get; set; } /// Gets or sets pattern to verify. public Regex Pattern { get; set; } /// Gets or sets a value indicating whether property value is required or not. public bool Required { get; set; } /// Gets or sets a value indicating whether this is an element requirement. public bool ElementRequirement { get; set; } /// Initializes a new instance of the class. /// This field will be required. public RequiredAttribute(bool required = true) { Required = required; } /// Initializes a new instance of the class. /// Limiting value to set. /// Whether set maximum parameter (true) or minimum parameter (false). /// This field will be required. public RequiredAttribute(float val, bool max, bool required = true) { if (max) Max = (decimal)val; else Min = (decimal)val; Required = required; } /// Initializes a new instance of the class. /// Minimum value to set. /// Maximum value to set. /// This field will be required. public RequiredAttribute(float min, float max, bool required = true) { Min = (decimal)min; Max = (decimal)max; Required = required; } /// Initializes a new instance of the class. /// Minimum value to set. /// Maximum value to set. /// Pattern to check. /// This field will be required. public RequiredAttribute(float min, float max, string pattern, bool required = true) { Min = (decimal)min; Max = (decimal)max; Pattern = new Regex(pattern, RegexOptions.Compiled); Required = required; } internal ValidateResult ValidateSimpleValue(DynamicPropertyInvoker dpi, object val) { return ValidateSimpleValue(dpi.Type, dpi.IsGnericEnumerable, val); } internal ValidateResult ValidateSimpleValue(Type type, bool isGnericEnumerable, object val) { if (val == null) { if (Required) return ValidateResult.ValueIsMissing; else return ValidateResult.Valid; } if (type.IsValueType) { if (val is decimal || val is long || val is int || val is float || val is double || val is short || val is byte || val is decimal? || val is long? || val is int? || val is float? || val is double? || val is short? || val is byte?) { decimal dec = Convert.ToDecimal(val); if (Min.HasValue && Min.Value > dec) return ValidateResult.ValueTooSmall; if (Max.HasValue && Max.Value < dec) return ValidateResult.ValueTooLarge; return ValidateResult.Valid; } else { var str = val.ToString(); if (Min.HasValue && Min.Value > str.Length) return ValidateResult.ValueTooShort; if (Max.HasValue && Max.Value < str.Length) return ValidateResult.ValueTooLong; if (Pattern != null && !Pattern.IsMatch(str)) return ValidateResult.ValueDontMatchPattern; return ValidateResult.Valid; } } else if (type.IsArray || isGnericEnumerable) { int? cnt = null; var list = val as IEnumerable; if (list != null) cnt = list.Count(); else { var enumerable = val as IEnumerable; if (enumerable != null) cnt = enumerable.Cast().Count(); } if (Min.HasValue && Min.Value > cnt) return ValidateResult.TooFewElementsInCollection; if (Max.HasValue && Max.Value < cnt) return ValidateResult.TooManyElementsInCollection; return ValidateResult.Valid; } else if (type == typeof(string)) { var str = (string)val; if (Min.HasValue && Min.Value > str.Length) return ValidateResult.ValueTooShort; if (Max.HasValue && Max.Value < str.Length) return ValidateResult.ValueTooLong; if (Pattern != null && !Pattern.IsMatch(str)) return ValidateResult.ValueDontMatchPattern; return ValidateResult.Valid; } return ValidateResult.NotSupported; } } /// Validation result enum. public enum ValidateResult { /// The valid value. Valid, /// The value is missing. ValueIsMissing, /// The value too small. ValueTooSmall, /// The value too large. ValueTooLarge, /// The too few elements in collection. TooFewElementsInCollection, /// The too many elements in collection. TooManyElementsInCollection, /// The value too short. ValueTooShort, /// The value too long. ValueTooLong, /// The value don't match pattern. ValueDontMatchPattern, /// The not supported. NotSupported, } /// Validation result. public class ValidationResult { /// Gets the property invoker. public DynamicPropertyInvoker Property { get; internal set; } /// Gets the requirement definition. public RequiredAttribute Requirement { get; internal set; } /// Gets the value that is broken. public object Value { get; internal set; } /// Gets the result. public ValidateResult Result { get;internal set;} } } }