/// <summary>
        ///  <para>Creates an <see cref="IDbJob{List{object}}"/> able to execute a reader based on the configured parameters.</para>
        /// <para>Valid types: <see cref="DataSet"/>, <see cref="DataTable"/>, <see cref="Dictionary{string,object}"/>, any .NET built-in type, or any struct or class with a parameterless constructor not assignable from <see cref="System.Collections.IEnumerable"/> (Note: only properties will be mapped).</para>
        ///  See also:
        ///  <seealso cref="DbCommand.ExecuteReader()"/>
        /// </summary>
        /// <remarks>
        /// This will use the <see cref="CommandBehavior.SingleResult"/> behavior by default.
        /// </remarks>
        /// <param name="type">The <see cref="Type"/> to use.</param>
        /// <param name="mapSettings">The <see cref="IColumnMapSetting"/> to use.</param>
        /// <param name="sql">The query text command to run against the data source.</param>
        /// <param name="param">The parameter to use. <see cref="DbJobParameterCollection.AddFor(object, bool, string, string)"/> restrictions apply. (Optional)</param>
        /// <param name="commandType">The <see cref="CommandType"/> to use. (Optional)</param>
        /// <param name="commandBehavior">The <see cref="CommandBehavior"/> to use. (Optional)</param>
        /// <param name="commandTimeout">The time in seconds to wait for the command to execute. (Optional)</param>
        /// <param name="flags">The flags to use. (Optional)</param>
        /// <returns>The <see cref="IDbJob{List{object}}"/>.</returns>
        public IDbJob <List <object> > ReadToList(
            Type type,
            IColumnMapSetting mapSettings,
            string sql,
            object param                    = null,
            CommandType commandType         = CommandType.Text,
            CommandBehavior?commandBehavior = null,
            int?commandTimeout              = null,
            DbJobCommandFlags flags         = DbJobCommandFlags.None)
        {
            if (type == null)
            {
                throw new ArgumentNullException("type cannot be null!");
            }

            return(new DbJob <List <object>, TDbConnection>
                   (
                       setting: _jobSetting,
                       state: new DbConnectorSimpleState {
                Flags = _flags
            },
                       onCommands: (conn, state) => BuildJobCommandForSimpleState(conn, state, mapSettings, sql, param, commandType, commandBehavior, commandTimeout, flags),
                       onExecute: (d, p) => OnExecuteReadToList(type, p)
                   ).SetOnError((d, e) => new List <object>()));
        }
        /// <summary>
        ///  <para>Creates an <see cref="IDbJob{object}"/> able to execute a reader based on the configured parameters.</para>
        ///  <para>Use this to load only a single row from the query result into an object.</para>
        /// <para>Valid types: <see cref="DataSet"/>, <see cref="DataTable"/>, <see cref="Dictionary{string,object}"/>, any .NET built-in type, or any struct or class with a parameterless constructor not assignable from <see cref="System.Collections.IEnumerable"/> (Note: only properties will be mapped).</para>
        ///  See also:
        ///  <seealso cref="DbCommand.ExecuteReader()"/>
        /// </summary>
        /// <remarks>
        /// This will use the <see cref="CommandBehavior.SingleResult"/> behavior by default.
        /// </remarks>
        /// <param name="type">The <see cref="Type"/> to use.</param>
        /// <param name="mapSettings">The <see cref="IColumnMapSetting"/> to use.</param>
        /// <param name="sql">The query text command to run against the data source.</param>
        /// <param name="param">The parameter to use. <see cref="DbJobParameterCollection.AddFor(object, bool, string, string)"/> restrictions apply. (Optional)</param>
        /// <param name="commandType">The <see cref="CommandType"/> to use. (Optional)</param>
        /// <param name="commandBehavior">The <see cref="CommandBehavior"/> to use. (Optional)</param>
        /// <param name="commandTimeout">The time in seconds to wait for the command to execute. (Optional)</param>
        /// <param name="flags">The flags to use. (Optional)</param>
        /// <returns>The <see cref="IDbJob{object}"/>.</returns>
        /// <exception cref="InvalidOperationException">The query result has more than one result.</exception>
        public IDbJob <object> ReadSingleOrDefault(
            Type type,
            IColumnMapSetting mapSettings,
            string sql,
            object param                    = null,
            CommandType commandType         = CommandType.Text,
            CommandBehavior?commandBehavior = null,
            int?commandTimeout              = null,
            DbJobCommandFlags flags         = DbJobCommandFlags.None)
        {
            if (type == null)
            {
                throw new ArgumentNullException("type cannot be null!");
            }

            return(new DbJob <object, TDbConnection>
                   (
                       setting: _jobSetting,
                       state: new DbConnectorSimpleState {
                Flags = _flags
            },
                       onInit: () => type.IsValueType ? Activator.CreateInstance(type) : null,
                       onCommands: (conn, state) => BuildJobCommandForSimpleState(conn, state, mapSettings, sql, param, commandType, commandBehavior, commandTimeout, flags),
                       onExecute: (d, p) => OnExecuteReadSingleOrDefault(type, p)
                   ));
        }
Esempio n. 3
0
 /// <summary>
 ///  <para>Creates an <see cref="IDbJob{IEnumerable{dynamic}}"/> able to execute a reader based on the configured parameters.</para>
 ///  <para>Use this to dynamically load the query results into an IEnumerable of <see cref="System.Dynamic.ExpandoObject"/>.</para>
 ///  See also:
 ///  <seealso cref="DbCommand.ExecuteReader()"/>
 /// </summary>
 /// <remarks>
 /// This will use the <see cref="CommandBehavior.SingleResult"/> behavior by default.
 /// </remarks>
 /// <param name="mapSettings">The <see cref="IColumnMapSetting"/> to use.</param>
 /// <param name="sql">The query text command to run against the data source.</param>
 /// <param name="param">The parameter to use. <see cref="DbJobParameterCollection.AddFor(object, bool, string, string)"/> restrictions apply. (Optional)</param>
 /// <param name="commandType">The <see cref="CommandType"/> to use. (Optional)</param>
 /// <param name="commandBehavior">The <see cref="CommandBehavior"/> to use. (Optional)</param>
 /// <param name="commandTimeout">The time in seconds to wait for the command to execute. (Optional)</param>
 /// <param name="flags">The flags to use. (Optional)</param>
 /// <returns>The <see cref="IDbJob{IEnumerable{dynamic}}"/>.</returns>
 public IDbJob <IEnumerable <dynamic> > Read(
     IColumnMapSetting mapSettings,
     string sql,
     object param                    = null,
     CommandType commandType         = CommandType.Text,
     CommandBehavior?commandBehavior = null,
     int?commandTimeout              = null,
     DbJobCommandFlags flags         = DbJobCommandFlags.None)
 {
     return(new DbJob <IEnumerable <dynamic>, TDbConnection>
            (
                setting: _jobSetting,
                state: new DbConnectorSimpleState {
         Flags = _flags
     },
                onCommands: (conn, state) => BuildJobCommandForSimpleState(conn, state, mapSettings, sql, param, commandType, commandBehavior, commandTimeout, flags),
                onExecute: (d, p) => OnExecuteReadDynamic(d, p)
            ).SetOnError((d, e) => Enumerable.Empty <dynamic>()));
 }
Esempio n. 4
0
 /// <summary>
 ///  <para>Creates an <see cref="IDbJob{dynamic}"/> able to execute a reader based on the configured parameters.</para>
 ///  <para>Use this to dynamically load only a single row from the query result into a <see cref="System.Dynamic.ExpandoObject"/>.</para>
 ///  See also:
 ///  <seealso cref="DbCommand.ExecuteReader()"/>
 /// </summary>
 /// <remarks>
 /// This will use the <see cref="CommandBehavior.SingleResult"/> behavior by default.
 /// </remarks>
 /// <param name="mapSettings">The <see cref="IColumnMapSetting"/> to use.</param>
 /// <param name="sql">The query text command to run against the data source.</param>
 /// <param name="param">The parameter to use. <see cref="DbJobParameterCollection.AddFor(object, bool, string, string)"/> restrictions apply. (Optional)</param>
 /// <param name="commandType">The <see cref="CommandType"/> to use. (Optional)</param>
 /// <param name="commandBehavior">The <see cref="CommandBehavior"/> to use. (Optional)</param>
 /// <param name="commandTimeout">The time in seconds to wait for the command to execute. (Optional)</param>
 /// <param name="flags">The flags to use. (Optional)</param>
 /// <returns>The <see cref="IDbJob{dynamic}"/>.</returns>
 /// <exception cref="InvalidOperationException">The query result has more than one result.</exception>
 public IDbJob <dynamic> ReadSingleOrDefault(
     IColumnMapSetting mapSettings,
     string sql,
     object param                    = null,
     CommandType commandType         = CommandType.Text,
     CommandBehavior?commandBehavior = null,
     int?commandTimeout              = null,
     DbJobCommandFlags flags         = DbJobCommandFlags.None)
 {
     return(new DbJob <dynamic, TDbConnection>
            (
                setting: _jobSetting,
                state: new DbConnectorSimpleState {
         Flags = _flags
     },
                onCommands: (conn, state) => BuildJobCommandForSimpleState(conn, state, mapSettings, sql, param, commandType, commandBehavior, commandTimeout, flags),
                onExecute: (d, p) => OnExecuteReadSingleOrDefaultDynamic(d, p)
            ));
 }
        /// <summary>
        ///  <para>Creates an <see cref="IDbJob{object}"/> to get the first column of the first row in the result
        ///  set returned by the query. All other columns and rows are ignored.</para>
        ///  See also:
        ///  <seealso cref="DbCommand.ExecuteScalar"/>
        /// </summary>
        /// <param name="mapSettings">The <see cref="IColumnMapSetting"/> to use.</param>
        /// <param name="sql">The query text command to run against the data source.</param>
        /// <param name="param">The parameter to use. <see cref="DbJobParameterCollection.AddFor(object, bool, string, string)"/> restrictions apply. (Optional)</param>
        /// <param name="commandType">The <see cref="CommandType"/> to use. (Optional)</param>
        /// <param name="commandBehavior">The <see cref="CommandBehavior"/> to use. (Optional)</param>
        /// <param name="commandTimeout">The time in seconds to wait for the command to execute. (Optional)</param>
        /// <param name="flags">The flags to use. (Optional)</param>
        /// <returns>The <see cref="IDbJob{object}"/>.</returns>
        public IDbJob <object> Scalar(
            IColumnMapSetting mapSettings,
            string sql,
            object param                    = null,
            CommandType commandType         = CommandType.Text,
            CommandBehavior?commandBehavior = null,
            int?commandTimeout              = null,
            DbJobCommandFlags flags         = DbJobCommandFlags.None)
        {
            return(new DbJob <object, TDbConnection>
                   (
                       setting: _jobSetting,
                       state: new DbConnectorSimpleState {
                Flags = _flags
            },
                       onCommands: (conn, state) => BuildJobCommandForSimpleState(conn, state, mapSettings, sql, param, commandType, commandBehavior, commandTimeout, flags),
                       onExecute: (d, p) =>
            {
                object scalar = p.Command.ExecuteScalar();

                return scalar != DBNull.Value ? scalar : null;
            }
                   ));
        }
 /// <summary>
 /// Creates an <see cref="IEnumerable{ColumnMap}"/> based on the provided type.
 /// </summary>
 /// <param name="odr">The <see cref="DbDataReader"/> to use.</param>
 /// <param name="objType">The <see cref="Type"/> to use.</param>
 /// <param name="settings">The <see cref="IColumnMapSetting"/> to use. (Optional)</param>
 /// <returns>The <see cref="IEnumerable{ColumnMap}"/>.</returns>
 public static IEnumerable <ColumnMap> GetColumnMaps(this DbDataReader odr, Type objType, IColumnMapSetting settings = null)
 {
     return(DbConnectorUtilities.GetColumnMaps(objType, odr.GetOrdinalColumnNames(settings), settings));
 }
        internal static OrdinalColumnMapLite[] GetOrdinalColumnNamesLite(this DbDataReader odr, IColumnMapSetting settings = null)
        {
            if (settings == null || (!settings.HasNamesToInclude && !settings.HasNamesToExclude))
            {
                var ordinalColumnMap = new OrdinalColumnMapLite[odr.FieldCount];

                for (int i = 0; i < odr.FieldCount; i++)
                {
                    ordinalColumnMap[i] = new OrdinalColumnMapLite {
                        Ordinal = i, Name = odr.GetName(i)
                    };
                }

                return(ordinalColumnMap);
            }
            else
            {
                bool hasNamesToInclude = settings.HasNamesToInclude;
                bool hasNamesToExclude = settings.HasNamesToExclude;

                var tempMap = new Queue <OrdinalColumnMapLite>(odr.FieldCount);

                for (int i = 0; i < odr.FieldCount; i++)
                {
                    string colName = odr.GetName(i);

                    if (!DbConnectorUtilities.IsColumnNameExcluded(colName, hasNamesToInclude, hasNamesToExclude, settings))
                    {
                        tempMap.Enqueue(new OrdinalColumnMapLite {
                            Ordinal = i, Name = colName
                        });
                    }
                }

                if (tempMap.Count > 0)
                {
                    var ordinalColumnMap = new OrdinalColumnMapLite[tempMap.Count];

                    for (int i = 0; tempMap.Count > 0; i++)
                    {
                        ordinalColumnMap[i] = tempMap.Dequeue();
                    }

                    return(ordinalColumnMap);
                }
                else
                {
                    return(null);
                }
            }
        }
        /// <summary>
        /// Reads data into a <see cref="DataSet"/>.
        /// </summary>
        /// <param name="odr">The <see cref="DataSet"/> to use.</param>
        /// <param name="isFirstResult">Set to true if only the first item result should be loaded.</param>
        /// <param name="token">The <see cref="CancellationToken"/> to use. (Optional)</param>
        /// <param name="settings">The <see cref="IColumnMapSetting"/> to use. (Optional)</param>
        /// <returns>The <see cref="DataSet"/>.</returns>
        public static DataSet ToDataSet(this DbDataReader odr, bool isFirstResult, CancellationToken token, IColumnMapSetting settings, DataSet projectedDataSet = null)
        {
            projectedDataSet = projectedDataSet ?? new DataSet();

            bool hasNext = true;

            while ((hasNext && odr.HasRows) || odr.NextResult())
            {
                if (token.IsCancellationRequested)
                {
                    return(projectedDataSet);
                }

                projectedDataSet.Tables.Add(odr.ToDataTable(isFirstResult, token, settings));

                hasNext = odr.NextResult();
            }

            return(projectedDataSet);
        }
        /// <summary>
        /// Reads data into a <see cref="DataTable"/>.
        /// </summary>
        /// <param name="odr">The <see cref="DbDataReader"/> to use.</param>
        /// <param name="isFirstResult">Set to true if only the first row should be loaded.</param>
        /// <param name="token">The <see cref="CancellationToken"/> to use. (Optional)</param>
        /// <param name="settings">The <see cref="IColumnMapSetting"/> to use. (Optional)</param>
        /// <returns>The <see cref="DataTable"/>.</returns>
        public static DataTable ToDataTable(this DbDataReader odr, bool isFirstResult, CancellationToken token, IColumnMapSetting settings)
        {
            var dt = new DataTable();

            if (odr.HasRows)
            {
                if (isFirstResult || (settings != null && (settings.HasNamesToInclude || settings.HasNamesToExclude)))
                {
                    var ordinalColumnMap = odr.GetOrdinalColumnNamesLite(settings);

                    if (ordinalColumnMap?.Length > 0)
                    {
                        foreach (var m in ordinalColumnMap)
                        {
                            dt.Columns.Add(m.Name);
                        }

                        if (isFirstResult)
                        {
                            if (odr.Read())
                            {
                                DataRow row = dt.NewRow();

                                for (int i = 0; i < ordinalColumnMap.Length; i++)
                                {
                                    row[i] = odr.GetValue(ordinalColumnMap[i].Ordinal);
                                }

                                dt.Rows.Add(row);
                            }
                        }
                        else
                        {
                            while (odr.Read())
                            {
                                if (token.IsCancellationRequested)
                                {
                                    break;
                                }

                                DataRow row = dt.NewRow();

                                for (int i = 0; i < ordinalColumnMap.Length; i++)
                                {
                                    row[i] = odr.GetValue(ordinalColumnMap[i].Ordinal);
                                }

                                dt.Rows.Add(row);
                            }
                        }
                    }
                }
                else
                {
                    dt.Load(odr);
                }
            }

            return(dt);
        }
 /// <summary>
 /// Creates an <see cref="IEnumerable{ColumnMap}"/> using the properties of the T type and the keys.
 /// </summary>
 /// <param name="ordinalColumnMap">The ordinal column map.</param>
 /// <param name="settings">The settings to use.</param>
 /// <returns>A list with the ColumnMap objects.</returns>
 /// <exception cref="System.InvalidCastException">Thrown when a property of <typeparamref name="T"/> does not match the located key type.</exception>
 internal static IEnumerable <ColumnMap> GetColumnMaps <T>(OrdinalColumnMap[] ordinalColumnMap, IColumnMapSetting settings)
 {
     return(GetColumnMaps(typeof(T), ordinalColumnMap, settings));
 }
        internal static bool IsColumnNameExcluded(string columnName, bool hasNamesToInclude, bool hasNamesToExclude, IColumnMapSetting settings)
        {
            if (hasNamesToInclude && hasNamesToExclude && (!settings.NamesToInclude.Contains(columnName) || settings.NamesToExclude.Contains(columnName)))
            {
                return(true);
            }
            else if (hasNamesToInclude && !settings.NamesToInclude.Contains(columnName))
            {
                return(true);
            }
            else if (hasNamesToExclude && settings.NamesToExclude.Contains(columnName))
            {
                return(true);
            }

            return(false);
        }
        internal static int GetJoinStartIndex(Type tType, OrdinalColumnMap[] ordinalColumnMap, IColumnMapSetting settings)
        {
            if (settings.Joins.TryGetValue(tType, out string columnName))
            {
                int splitStartIndex = Array.FindIndex(ordinalColumnMap, c => !c.IsMapped && c.Name == columnName);

                if (splitStartIndex == -1)
                {
                    return(0);
                }

                return(splitStartIndex);
            }

            return(0);
        }
        private static IEnumerable <ColumnMap> BuildColumnMaps(
            Type tType,
            OrdinalColumnMap[] keys,
            ColumnMapBuilderState state,
            IColumnMapSetting settings)
        {
            state.ProcessedTypes.Add(tType);
            PropertyInfo[] props          = tType.GetProperties(_bindingFlagInstancePublic);
            int            joinStartIndex = state.HasJoins ? GetJoinStartIndex(tType, keys, settings) : 0;


            for (int i = 0; i < props.Length; i++)
            {
                var p = props[i];

                if (state.MappedCount == state.NonExcludedCount)
                {
                    break;
                }

                if (p.CanWrite && p.GetCustomAttribute <NotMappedAttribute>() == null)
                {
                    Type propertyType       = p.PropertyType;
                    Type nullUnderlyingType = Nullable.GetUnderlyingType(propertyType);

                    if (
                        state.HasJoins &&
                        settings.Joins.ContainsKey(propertyType) &&
                        !state.ProcessedTypes.Contains(propertyType) &&
                        !_directTypeMap.Contains(propertyType) &&
                        ((propertyType.IsClass && propertyType.GetConstructor(Type.EmptyTypes) != null) || (propertyType.IsValueType && !(propertyType.IsEnum || (nullUnderlyingType?.IsEnum ?? false)))) &&
                        !propertyType.IsArray &&
                        !typeof(IEnumerable).IsAssignableFrom(propertyType) &&
                        !typeof(IListSource).IsAssignableFrom(propertyType)
                        )
                    {
                        var childrenMaps = BuildColumnMaps
                                           (
                            propertyType,
                            keys,
                            state,
                            settings
                                           ).OrderBy(m => m.Column.Ordinal).ToArray();

                        if (childrenMaps.Length > 0)
                        {
                            var parentMap = new ColumnParentMap
                            {
                                SetMethod = p.GetSetMethod(false),
                                PropInfo  = p,
                                Children  = childrenMaps
                            };

                            yield return(new ColumnMap {
                                Column = childrenMaps[0].Column, ParentMap = parentMap
                            });
                        }

                        continue;
                    }


                    string propColName = p.GetColumnAttributeName();

                    if (state.HasAliases && settings.Aliases.TryGetValue(tType, out Dictionary <string, string> aliasMap))
                    {
                        if (aliasMap != null && aliasMap.TryGetValue(propColName, out string alias))
                        {
                            propColName = alias;
                        }
                    }


                    var keyIndex = Array.FindIndex(keys, joinStartIndex, c => !c.IsMapped && c.Name == propColName);

                    if (keyIndex >= 0)
                    {
                        var  ordinalMap     = keys[keyIndex];
                        Type underlyingType = (nullUnderlyingType ?? propertyType);

                        ThrowIfFailedToMatchColumnTypeByNames(ordinalMap.FieldType, underlyingType, ordinalMap.Name, p.Name);


                        yield return(new ColumnMap
                        {
                            UnderlyingType = underlyingType,
                            SetMethod = p.GetSetMethod(false),
                            PropInfo = p,
                            Column = ordinalMap
                        });


                        ordinalMap.IsMapped = true;
                        state.MappedCount++;
                    }
#if DEBUG
                    else
                    {
                        Debug.WriteLine("Column name " + propColName + " not found in column schema for property " + propertyType + " of object " + tType);
                    }
#endif
                }
            }
        }
        /// <summary>
        /// Creates an <see cref="IEnumerable{ColumnMap}"/> using the properties of the tType and the keys.
        /// </summary>
        /// <param name="tType">The type to use.</param>
        /// <param name="ordinalColumnMap">The ordinal column map.</param>
        /// <param name="settings">The settings to use.</param>
        /// <returns>A list with the ColumnMap objects.</returns>
        /// <exception cref="System.InvalidCastException">Thrown when a property of <paramref name="tType"/> does not match the located key type.</exception>
        internal static IEnumerable <ColumnMap> GetColumnMaps(Type tType, OrdinalColumnMap[] ordinalColumnMap, IColumnMapSetting settings)
        {
            if (ordinalColumnMap == null || ordinalColumnMap.Length == 0)
            {
                return(Enumerable.Empty <ColumnMap>());
            }

            return(BuildColumnMaps
                   (
                       tType,
                       ordinalColumnMap,
                       settings == null
                ? new ColumnMapBuilderState
            {
                NonExcludedCount = ordinalColumnMap.Length,
                ProcessedTypes = new HashSet <Type>()
            }
                : new ColumnMapBuilderState
            {
                NonExcludedCount = ordinalColumnMap.Length,
                ProcessedTypes = new HashSet <Type>(),
                HasJoins = settings.HasJoins,
                HasAliases = settings.HasAliases,
            },
                       settings
                   ).OrderBy(m => m.Column.Ordinal).ToArray());
        }
        public static T GetMappedObject <T>(this DbDataReader odr, IColumnMapSetting settings = null)
        {
            var columnMaps = DbConnectorUtilities.GetColumnMaps(typeof(T), odr.GetOrdinalColumnNames(settings), settings);

            return(odr.GetMappedObject <T>(columnMaps));
        }
        public static object GetMappedObject(this DbDataReader odr, Type objType, IColumnMapSetting settings = null)
        {
            var columnMaps = DbConnectorUtilities.GetColumnMaps(objType, odr.GetOrdinalColumnNames(settings), settings);

            return(odr.GetMappedObject(objType, columnMaps));
        }
        internal static int GetJoinStartIndex(Type tType, OrdinalColumnMap[] ordinalColumnMap, IColumnMapSetting settings)
        {
            if (settings.Splits.TryGetValue(tType, out string columnName))
            {
                int splitStartIndex = Array.FindIndex(ordinalColumnMap, c => !c.IsMapped && string.Equals(c.Name, columnName, StringComparison.OrdinalIgnoreCase));

                if (splitStartIndex == -1)
                {
                    return(0);
                }

                return(splitStartIndex);
            }

            return(0);
        }