/// <summary> /// Extracts a full object from the current record in a DataReader, mapping return columns to object constructer arguments. /// Use this for mapping to immutable types. /// </summary> public static T ConstructObject <T>(this IDataReader reader, DataMappingOptions options = null) { if (options == null) { options = new DataMappingOptions(); } Type type = typeof(T); var ctor = type.GetConstructors() .OrderByDescending(x => x.GetParameters().Length) .FirstOrDefault(); var ctorParameters = ctor.GetParameters(); var columns = from DataRow row in reader.GetSchemaTable().Rows select new { Name = row["ColumnName"].ToString().ToLower(), Ordinal = Convert.ToInt32(row["ColumnOrdinal"]), DatabaseType = (Type)row["DataType"] }; var mappings = (from parameter in ctorParameters join column in columns on parameter.Name.ToLower() equals column.Name select new { Ordinal = column.Ordinal, DatabaseType = column.DatabaseType, ParameterType = parameter.ParameterType }).ToList(); // Validate if (ctorParameters.Length > mappings.Count) { throw new InvalidOperationException( "Invalid mappings detected. Attempting to construct an object of type " + type.FullName + " with missing parameter information.\r\n" + "Ctor parameters: " + String.Join(", ", ctorParameters.Select(x => string.Format("{0} {1}", x.ParameterType, x.Name))) + "\r\n" + "Database fields: " + String.Join(", ", columns.Select(x => string.Format("{0} {1}", x.DatabaseType, x.Name)))); } var parameterValues = mappings.Select(mapping => GetValueFromDataReader(reader, mapping.Ordinal, mapping.DatabaseType, mapping.ParameterType, options)) .ToArray(); var result = ctor.Invoke(parameterValues); return((T)result); }
/// <summary> /// Extracts a full object from the current record in a DataReader, mapping return columns to object properties. /// Allows custom mappings of DataReader field values to an object result value via customMappings argument. /// </summary> public static T GetObject <T>(this IDataReader reader, IDictionary <string, Func <IDataReader, string, object> > customMappings = null, DataMappingOptions options = null) where T : new() { Type type = typeof(T); T obj = new T(); if (options == null) { options = new DataMappingOptions(); } var schema = reader.GetSchemaTable(); foreach (DataRow row in schema.Rows) { var column = row["ColumnName"].ToString(); var ordinal = Convert.ToInt32(row["ColumnOrdinal"]); var dbType = (Type)row["DataType"]; var prop = type.GetProperty(column); if (prop != null && prop.CanWrite) { if (customMappings != null && customMappings.ContainsKey(column)) { object value = customMappings[column](reader, column); prop.SetValue(obj, value); } else { object value = GetValueFromDataReader(reader, ordinal, dbType, prop.PropertyType, options); if (value != null) { prop.SetValue(obj, value); } } } } return(obj); }
private static object GetValueFromDataReader(IDataReader reader, int column, Type sourceType, Type destinationType, DataMappingOptions options) { object value = reader.GetValue(column); if (value == DBNull.Value) { value = null; } if (value != null) { if (sourceType != destinationType) { if (destinationType.IsGenericType && destinationType.GetGenericTypeDefinition() == typeof(Nullable <>)) { destinationType = Nullable.GetUnderlyingType(destinationType); } } if (sourceType == typeof(DateTime)) { //all of our dataTime values from SQL Server will be interpreted as local time because there is no offset stored, but many of the values are actually UTC, so we need to be able to read them correctly value = DateTime.SpecifyKind((DateTime)value, options.ReadDateTimeAs); } if (destinationType.IsEnum) { if (value is string) { value = Enum.Parse(destinationType, value.ToString(), true); } else { var iValue = Convert.ChangeType(value, Enum.GetUnderlyingType(destinationType)); value = Enum.ToObject(destinationType, iValue); } } else if (sourceType != destinationType) { if (destinationType == typeof(string)) { value = value.ToString(); } else { value = Convert.ChangeType(value, destinationType); } } } return(value); }