/// <summary> /// Convert an Oracle entity/object name (table, package, argument, column etc.) to a valid C# equivalent /// </summary> /// <param name="oracleArgName"></param> /// <param name="useCamelCase">convert to camelCase, otherwise defaults to PascalCase</param> /// <returns></returns> internal static string ConvertOracleNameToCSharpName(string oracleName, bool useCamelCase) { if (String.IsNullOrEmpty(oracleName)) { return(null); // this occurs with a return arg } String oracleNameAdjusted = oracleName; // replace special characters with alphabetic equivalent oracleNameAdjusted = oracleNameAdjusted.Replace(@"!", "exclamationpoint" + CHARACTER_ABBREV); oracleNameAdjusted = oracleNameAdjusted.Replace(@"@", "at" + CHARACTER_ABBREV); oracleNameAdjusted = oracleNameAdjusted.Replace(@"#", "pound" + CHARACTER_ABBREV); oracleNameAdjusted = oracleNameAdjusted.Replace(@"$", "dollar" + CHARACTER_ABBREV); oracleNameAdjusted = oracleNameAdjusted.Replace(@"%", "percent" + CHARACTER_ABBREV); oracleNameAdjusted = oracleNameAdjusted.Replace(@"^", "caret" + CHARACTER_ABBREV); oracleNameAdjusted = oracleNameAdjusted.Replace(@"&", "ampersand" + CHARACTER_ABBREV); oracleNameAdjusted = oracleNameAdjusted.Replace(@"*", "asterisk" + CHARACTER_ABBREV); oracleNameAdjusted = oracleNameAdjusted.Replace(@"-", "dash" + CHARACTER_ABBREV); oracleNameAdjusted = oracleNameAdjusted.Replace(@"+", "plus" + CHARACTER_ABBREV); oracleNameAdjusted = oracleNameAdjusted.Replace(@"=", "equals" + CHARACTER_ABBREV); oracleNameAdjusted = oracleNameAdjusted.Replace(@".", "period" + CHARACTER_ABBREV); oracleNameAdjusted = oracleNameAdjusted.Replace(@"?", "questionmark" + CHARACTER_ABBREV); oracleNameAdjusted = oracleNameAdjusted.Replace(@":", "colon" + CHARACTER_ABBREV); oracleNameAdjusted = oracleNameAdjusted.Replace(@";", "semicolon" + CHARACTER_ABBREV); String cSharpName = (useCamelCase ? CaseConverter.ConvertUnderscoreDelimitedToCamelCase(oracleNameAdjusted) : CaseConverter.ConvertUnderscoreDelimitedToPascalCase(oracleNameAdjusted)); if (Char.IsDigit(cSharpName, 0)) { cSharpName = (useCamelCase ? "t" : "T") + "he" + cSharpName; // a C# arg cannot start with number } if (CSharp.IsKeyword(cSharpName)) { cSharpName = cSharpName + "Cs"; // append text to avoid the C# keyword } return(cSharpName); }
/// <summary> /// Return a generic list of objects fetched from a data reader using type-specific read method /// </summary> /// <typeparam name="T"></typeparam> /// <param name="reader"></param> /// <param name="readMethod"></param> /// <returns></returns> //public static List<T> ReadResult<T>(OracleDataReader reader, Func<OracleDataReader, T> readMethod) { // // iterate reader and create list of BOs using they type's reader method // List<T> list = new List<T>(); // if (reader.HasRows) while (reader.Read() == true) list.Add(readMethod(reader)); // return list; //} /// <summary> /// Return a Datatable fetched from a data reader /// </summary> /// <param name="reader">Reader prepared for fetching result set</param> /// <returns></returns> public static DataTable ReadResult(OracleDataReader reader, bool convertColumnNameToTitleCaseInCaption = false, UInt32?optionalMaximumNumberOfRowsToRead = null) { // determine name and type of each column in result set and build empty datatable DataTable dt = new DataTable(); DataColumn dc; List <string> colName = new List <string>(); // column names and respective Oracle type from the reader List <Column> readerColumns = GetReaderColumnTypes(reader); // build empty datatable with column names and correct C# type foreach (Column col in readerColumns) { // add column to list colName.Add(col.ColumnName); // create data column object based on Oracle column type, save column type to array if (col.ColumnType == typeof(OracleString)) { dc = new DataColumn(col.ColumnName, typeof(System.String)); } else if (col.OracleDataTypeName.Equals(OracleDbType.BinaryDouble.ToString())) { dc = new DataColumn(col.ColumnName, typeof(System.Double)); } else if (col.OracleDataTypeName.Equals(OracleDbType.BinaryFloat.ToString())) { dc = new DataColumn(col.ColumnName, typeof(System.Single)); } else if (col.ColumnType == typeof(OracleDecimal)) { dc = new DataColumn(col.ColumnName, typeof(OracleDecimal)); } else if (col.ColumnType == typeof(OracleDate)) { dc = new DataColumn(col.ColumnName, typeof(OracleDate)); } else if (col.ColumnType == typeof(OracleTimeStamp)) { dc = new DataColumn(col.ColumnName, typeof(OracleTimeStamp)); } else if (col.ColumnType == typeof(OracleTimeStampLTZ)) { dc = new DataColumn(col.ColumnName, typeof(OracleTimeStampLTZ)); } else if (col.ColumnType == typeof(OracleTimeStampTZ)) { dc = new DataColumn(col.ColumnName, typeof(OracleTimeStampTZ)); } else { dc = new DataColumn(col.ColumnName, typeof(Object)); } //throw new Exception("Oracle column type not recognized in 'DataTable Hydrator.ReadResult()' for database column " + col.ColumnName); dc.Caption = convertColumnNameToTitleCaseInCaption ? CaseConverter.ConvertSnakeCaseToLabel(col.ColumnName) : col.ColumnName ; dt.Columns.Add(dc); } // add rows to data table DataRow drow; Int32 numRowsRead = 0; if (reader != null && reader.HasRows) { while (reader.Read()) { // create the row and set column data based on result set row drow = dt.NewRow(); for (int c = 0; c < colName.Count; c++) { if (reader.IsDBNull(reader.GetOrdinal(colName[c]))) { drow[colName[c]] = DBNull.Value; } else if (readerColumns[c].OracleDataTypeName.Equals(OracleDbType.BinaryDouble.ToString())) { drow[colName[c]] = (Double?)reader.GetDouble(reader.GetOrdinal(colName[c])); } else if (readerColumns[c].OracleDataTypeName.Equals(OracleDbType.BinaryFloat.ToString())) { drow[colName[c]] = (Single?)reader.GetFloat(reader.GetOrdinal(colName[c])); } else if (readerColumns[c].ColumnType == typeof(OracleString)) { drow[colName[c]] = (String)reader.GetOracleString(reader.GetOrdinal(colName[c])); } else if (readerColumns[c].ColumnType == typeof(OracleDecimal)) { drow[colName[c]] = (OracleDecimal?)OracleDecimal.SetPrecision(reader.GetOracleDecimal(reader.GetOrdinal(colName[c])), 28); } else if (readerColumns[c].ColumnType == typeof(OracleDate)) { drow[colName[c]] = (OracleDate?)reader.GetOracleDate(reader.GetOrdinal(colName[c])); } else if (readerColumns[c].ColumnType == typeof(OracleTimeStamp)) { drow[colName[c]] = (OracleTimeStamp?)reader.GetOracleTimeStamp(reader.GetOrdinal(colName[c])); } else if (readerColumns[c].ColumnType == typeof(OracleTimeStampLTZ)) { drow[colName[c]] = (OracleTimeStampLTZ?)reader.GetOracleTimeStampLTZ(reader.GetOrdinal(colName[c])); } else if (readerColumns[c].ColumnType == typeof(OracleTimeStampTZ)) { drow[colName[c]] = (OracleTimeStampTZ?)reader.GetOracleTimeStampTZ(reader.GetOrdinal(colName[c])); } else { drow[colName[c]] = reader.GetValue(reader.GetOrdinal(colName[c])); } } // Add the hydrated row to the datatable dt.Rows.Add(drow); if (optionalMaximumNumberOfRowsToRead != null) { numRowsRead++; if (numRowsRead >= optionalMaximumNumberOfRowsToRead) { break; } } } } // this is necessary in order for DataRowVersion status to be correct dt.AcceptChanges(); return(dt); }
/// <summary> /// Build list of column to property/field mappings for a given C# class type and a data reader /// </summary> /// <typeparam name="T"></typeparam> /// <param name="reader"></param> /// <param name="mapByPosition"></param> /// <param name="allowUnmappedColumns"></param> /// <returns></returns> private static List <ColumnMapping> BuildMappings <T>(OracleDataReader reader, bool mapByPosition, bool allowUnmappedColumns) { // extract all settable public properties of the type (includes inherited) PropertyInfo[] properties = typeof(T).GetProperties(BindingFlags.Public | BindingFlags.Instance); // if mapping by name, extract all settable protected and private fields (includes inherited) FieldInfo[] fields = null; if (!mapByPosition) { fields = typeof(T).GetFields(BindingFlags.NonPublic | BindingFlags.Instance); } // when mapping by position, filter out properties that do not have a map position set in the attribute if (mapByPosition) { properties = properties.Select(x => new { Property = x, Attribute = (HydratorMapAttribute)Attribute.GetCustomAttribute(x, typeof(HydratorMapAttribute)) }) .Where(x => x.Attribute != null && x.Attribute.Position >= 0).OrderBy(x => x.Attribute.Position) // important to order by position .Select(x => x.Property).ToArray(); // Never allow the number of properties with the position attribute to be greater than the column count. This means the BO is looking for // a column that is not there. if (properties.Length > reader.FieldCount) { throw new Exception("Hydrator.BuildMappings<T>() - number of settable properties with a position attribute in " + typeof(T).FullName + "(" + properties.Length + ") is greater than the number of columns in the reader(" + reader.FieldCount + ")."); // If not allowed, the number of properties with the position attribute cannot be less than the column count, meaning no column should be ignored. } else if (!allowUnmappedColumns && properties.Length < reader.FieldCount) { throw new Exception("Hydrator.BuildMappings<T>() - number of settable properties with a position attribute in " + typeof(T).FullName + "(" + properties.Length + ") is less than the number of columns the reader(" + reader.FieldCount + "), meaning a column has been unmapped."); } } List <ColumnMapping> mappings = new List <ColumnMapping>(); // holds all column mappings for a result set for (int c = 0; c < reader.FieldCount; c++) // loop reader columns { PropertyInfo property; // map by position works only with properties (not fields) if (mapByPosition) { if (c > properties.Length - 1) { break; // there are more columns than map properties, so our mapping is complete } else { property = properties[c]; // get the next property to be mapped } // Always check property position against column position at this point since the properties are sorted. If they // don't match, something is definitely wrong in the attribute settings. int propertyMapPosition = ((HydratorMapAttribute)Attribute.GetCustomAttribute(property, typeof(HydratorMapAttribute))).Position; if (c != propertyMapPosition) { throw new Exception("Hydrator.BuildMappings<T>() - property map position mismatch with reader columns near property position " + propertyMapPosition.ToString() + " on class " + typeof(T).FullName + "." + " Check for duplicate or missing position values on properties."); } // valid property found, add completed mapping to our list mappings.Add(new ColumnMapping(new Column(reader.GetName(c), reader.GetProviderSpecificFieldType(c), reader.GetDataTypeName(c)), property)); // mapping by name } else { // look first for an _underscorePrefixedCamelCase field, and then a camelCase field (both are non-public) FieldInfo field = Array.Find(fields, f => f.Name == CaseConverter.ConvertSnakeCaseToCamelCasePrefixedWithUnderscore(reader.GetName(c))) ?? Array.Find(fields, f => f.Name == CaseConverter.ConvertSnakeCaseToCamelCase(reader.GetName(c))); if (field != default(FieldInfo)) { // valid field found, add completed column mapping to our list mappings.Add(new ColumnMapping(new Column(reader.GetName(c), reader.GetProviderSpecificFieldType(c), reader.GetDataTypeName(c)), field)); } else // otherwise, look for a PascalCase property (public) since field not found { property = Array.Find(properties, p => p.Name == CaseConverter.ConvertSnakeCaseToPascalCase(reader.GetName(c))); if (property != default(PropertyInfo)) // this is equivalent to a "not null" compare // valid property found, add completed mapping to our list { mappings.Add(new ColumnMapping(new Column(reader.GetName(c), reader.GetProviderSpecificFieldType(c), reader.GetDataTypeName(c)), property)); } else { // a property does not exist, so the column has neither property nor field to map to if (allowUnmappedColumns) { continue; // unmapped column will be ignored - a "silent failed mapping" } else { throw new Exception("Hydrator.BuildMappings() - Could not find an _underscorePrefixedCamelCase non-public field, " + "a camelCase non-public field, nor a PascalCase public property on " + typeof(T).FullName + " for column " + reader.GetName(c)); } } } // if field not found } // mapping by name } // loop reader columns return(mappings); }