/// <summary>
        /// Populates the properties of an object from a single DataReader row using
        /// Reflection by matching the DataReader fields to a public property on
        /// the object passed in. Unmatched properties are left unchanged.
        ///
        /// You need to pass in a data reader located on the active row you want
        /// to serialize.
        ///
        /// This routine works best for matching pure data entities and should
        /// be used only in low volume environments where retrieval speed is not
        /// critical due to its use of Reflection.
        /// </summary>
        /// <param name="reader">Instance of the DataReader to read data from. Should be located on the correct record (Read() should have been called on it before calling this method)</param>
        /// <param name="instance">Instance of the object to populate properties on</param>
        /// <param name="fieldsToSkip">Optional - A comma delimited list of object properties that should not be updated</param>
        /// <param name="piList">Optional - Cached PropertyInfo dictionary that holds property info data for this object</param>
        public static void DataReaderToObject(this IDataReader reader, ref object instance, Type type = null, string fieldsToSkip = null, Dictionary <string, PropertyInfo> piList = null)
        {
            if (reader.IsClosed)
            {
                throw new InvalidOperationException("Data reader cannot be used because it's already closed");
            }

            var _type = instance == null ? type : instance.GetType();

            if (_type.IsNullableOrBasicType())
            {
                var value = reader[0];

                if (value != null && !DBNull.Value.Equals(value))
                {
                    if (_type == TypeHelper.TypeOfString)
                    {
                        instance = Activator.CreateInstance(TypeHelper.TypeOfString, SafeClrConvert.ToString(value).ToCharArray());
                    }
                    else
                    {
                        instance = SafeClrConvert.ChangeType(value, _type);
                    }
                }

                return;
            }

            if (string.IsNullOrEmpty(fieldsToSkip))
            {
                fieldsToSkip = string.Empty;
            }
            else
            {
                fieldsToSkip = "," + fieldsToSkip + ",";
            }

            fieldsToSkip = fieldsToSkip.ToLower();

            // create a dictionary of properties to look up
            // we can pass this in so we can cache the list once
            // for a list operation

            if (piList == null)
            {
                piList = new Dictionary <string, PropertyInfo>();
                var props = _type.GetProperties(BindingFlags.Instance | BindingFlags.Public);

                foreach (var prop in props)
                {
                    piList.Add(prop.Name.ToLower(), prop);
                }
            }

            for (var index = 0; index < reader.FieldCount; index++)
            {
                var name = reader.GetName(index).ToLower();

                if (piList.ContainsKey(name))
                {
                    var prop = piList[name];

                    if (fieldsToSkip.Contains("," + name + ","))
                    {
                        continue;
                    }

                    if ((prop != null) && prop.CanWrite)
                    {
                        var val = reader.GetValue(index);

                        prop.SetValue(instance, (val == DBNull.Value) ? null : val, null);
                    }
                }
            }
        }