public static string ConvertNullableSQLToCSharp(string sqlTypeCode, bool isNullable) { string typeCode = TypeConvertor.ConvertSQLToCSharp(sqlTypeCode); if (isNullable && typeCode != "string") { typeCode += "?"; } return(typeCode); }
private static void AddMember(ModelDef modelDef, string memberName, Type type, string requiredNamespace) { string typeCode = type.Name; string typeNamespace = type.Namespace; if (type.Namespace != requiredNamespace) { string vernacularCode = TypeConvertor.ConvertCLRToVernacular(type.ToString()); if (vernacularCode != null) { typeCode = vernacularCode; typeNamespace = null; } } /* TODO: better to use IsClass or IsByRef ? */ bool isReference = type.IsClass || type.IsInterface; if (memberName == "State") { Shared.Info("State"); } if (typeCode == "string") { isReference = false; } /* TODO: not sure if this should rewrite the type here or not */ /* TODO: the Interface rewriting should be an option */ if (type.IsInterface && typeCode.StartsWith("I")) { typeCode = typeCode.Substring(1); } PropertyDef propertyDef = new PropertyDef { PropertyName = memberName, PropertyTypeCode = typeCode, PropertyTypeNamespace = typeNamespace, IsInterface = type.IsInterface, IsReference = isReference, IsEnum = type.IsEnum }; modelDef.PropertyDefMap[memberName] = propertyDef; }
/* TODO: GetSchemaTable was failing on SQL Server Express, not sure why. * It's not that it needed data rows in the table. * Does it need data actually returned? */ private void PopulateFields2008(ProcedureDef procedureDef) { /* MSSQL prior to 2012 might need this, we can verify if there's a client to test against at some point */ /* * call the procedure to see the result set * */ using (SqlTransaction trans = this.SqlConnection.BeginTransaction()) { using (SqlCommand command = new SqlCommand() { CommandText = procedureDef.ProcedureName, CommandType = CommandType.StoredProcedure, Connection = this.SqlConnection, Transaction = trans }) { foreach (ParameterDef parameterDef in procedureDef.ParameterDefMap.Values) { ParameterDirection direction; if (parameterDef.IsOutParameter) { direction = ParameterDirection.Output; } else { direction = ParameterDirection.Input; } var parameter = new SqlParameter { ParameterName = parameterDef.ParameterName, Direction = direction, Size = parameterDef.ParameterSize, SqlDbType = GetSqlDbType(parameterDef.ParameterDataTypeCode) }; parameter.Value = DBNull.Value; command.Parameters.Add(parameter); } DataTable tableSchema; /* we really want all 3 behaviors, and within a transaction, but the two * behaviors CommandBehavior.SchemaOnly and CommandBehavior.KeyInfo fail * to give us a schema table to read. So we have to get a result within * a transaction. There's nothing super wrong with that, but it feels * dirty. Here are the results of combinations we've tried (all with SQL * Server Express 2014): * No behaviors, no transaction : succeeded * CommandBehavior.SchemaOnly alone, no transaction : failed * CommandBehavior.SchemaOnly| CommandBehavior.KeyInfo, no trans : failed * CommandBehavior.SchemaOnly| CommandBehavior.SingleResult, no trans : failed * CommandBehavior.SchemaOnly | CommandBehavior.KeyInfo | CommandBehavior.SingleResult, no trans : failed * CommandBehavior.KeyInfo, no trans : failed * CommandBehavior.KeyInfo | CommandBehavior.SingleResult, no trans : failed * CommandBehavior.SingleResult, no trans : succeeded * No behaviors, with trans : succeeded * CommandBehavior.SingleResult, with trans : succeeded */ using (var reader = command.ExecuteReader(CommandBehavior.SingleResult)) { reader.Read(); tableSchema = reader.GetSchemaTable(); } trans.Rollback(); if (tableSchema == null) { throw new ApplicationException("Sampling of procedure " + procedureDef.ProcedureName + " yielded no schema."); } foreach (DataRow row in tableSchema.Rows) { /* IsIdentity */ /* * int columnSize = (int)row["ColumnSize"]; * string sqlTypeCode = * bool isReadOnly = (bool)row["IsReadOnly"]; * bool isPrimaryKey = (bool)row["IsKey"]; * bool isAutoIncrement = (bool)row["IsAutoIncrement"]; */ string fieldName = row["ColumnName"].ToString(); /* TODO: on views, these BaseTableName and BaseColumnName are for the view, not the source table like we get for MSSQL2012 */ string baseTableName = row["BaseTableName"].ToString(); string baseColumnName = row["BaseColumnName"].ToString(); bool isNullable = (bool)row["AllowDBNull"]; string dataTypeCode = TypeConvertor.ConvertCLRToVernacular(row["DataType"].ToString()); FieldDef fieldDef = new FieldDef { FieldName = fieldName, ProcedureDef = procedureDef, DataTypeCode = dataTypeCode, IsNullable = isNullable, BaseTableName = baseTableName, BaseColumnName = baseColumnName }; procedureDef.FieldDefMap[fieldDef.FieldName] = fieldDef; } } } }
private void PopulateFields2012(ProcedureDef procedureDef) { /* pass procedureName, then a string of parameters (not necessary), * browserInfo should be 0 to return only the outputted columns and not join columns * and finally browserInfo must be 1 to get source table info on second call */ var validNameList = new List <string>(); using (SqlCommand command = new SqlCommand() { CommandText = $@"SELECT FieldName=name FROM sys.dm_exec_describe_first_result_set('{procedureDef.ProcedureName}', NULL, 0)", CommandType = CommandType.Text, Connection = this.SqlConnection }) { using (var reader = command.ExecuteReader()) { while (reader.Read()) { IDataReader row = reader; string fieldName = row["FieldName"].ToString(); validNameList.Add(fieldName); } } } using (SqlCommand command = new SqlCommand() { CommandText = $@"SELECT FieldName=name, DataTypeCode=system_type_name, IsNullable = is_nullable, BaseTableName = source_table, BaseColumnName = source_column FROM sys.dm_exec_describe_first_result_set('{procedureDef.ProcedureName}', NULL, 1)", CommandType = CommandType.Text, Connection = this.SqlConnection }) { var regex = new Regex("^(\\w+)\\(\\d+(,\\d+)?\\)$"); using (var reader = command.ExecuteReader()) { while (reader.Read()) { IDataReader row = reader; string fieldName = row["FieldName"].ToString(); if (fieldName == "") { throw new Exception("MSSQL inspection for procedure " + procedureDef.ProcedureName + " returned a field with no name. Check the stored procedure for invalid characters."); } if (validNameList.Contains(fieldName) == false) { // Shared.Info("Skipping superfluous field " + fieldName); } else { string sqlDataTypeCode = row["DataTypeCode"].ToString(); /* MSSQL2012 will send a char length too, e.g. varchar(10), decimal(18,0) */ var match = regex.Match(sqlDataTypeCode); if (match.Success) { sqlDataTypeCode = match.Groups[1].Value; } string dataTypeCode; try { dataTypeCode = TypeConvertor.ConvertSQLToCSharp(sqlDataTypeCode); } catch { Shared.Info("Call to ConvertSQLToCSharp for procedure " + procedureDef.ProcedureName + ", field named \"" + fieldName + "\", of SQL data type \"" + sqlDataTypeCode + "\". This can happen if you have a stored procedure that is referencing a column that no longer exists for a table. Check that the procedure will run on its own."); throw; } bool isNullable = (bool)row["IsNullable"]; /* max_length, precision, scale */ /* TODO: using result set procedure does not populate source * table or column, which is as close to these fields * as I could find */ string baseTableName = row["BaseTableName"].ToString(); string baseColumnName = row["BaseColumnName"].ToString(); // Shared.Info($"DEBUG:procedureName={procedureDef.ProcedureName}, fieldName={fieldName}, baseTableName={baseTableName}, baseColumnName={baseColumnName}"); FieldDef fieldDef = new FieldDef { FieldName = fieldName, ProcedureDef = procedureDef, DataTypeCode = dataTypeCode, IsNullable = isNullable, BaseTableName = baseTableName, BaseColumnName = baseColumnName }; procedureDef.FieldDefMap[fieldDef.FieldName] = fieldDef; } } } } }
public List <ProcedureDef> MakeProcedureDefList(string databaseName, string moduleName, Dictionary <string, TableDef> tableDefMap) { /* var tableMap = {}; * var functionMap = {}; * var tableArray = []; * * */ MySqlConnection conn = this.mySqlConnection; var globalProcedureDefMap = new Dictionary <string, ProcedureDef>(); using (MySqlCommand command = new MySqlCommand() { CommandText = "SELECT * FROM INFORMATION_SCHEMA.ROUTINES WHERE ROUTINE_SCHEMA='" + databaseName + "' AND ROUTINE_NAME LIKE '" + moduleName + "_%_%' AND ROUTINE_TYPE='PROCEDURE';", CommandType = CommandType.Text, Connection = conn }) { using (var reader = command.ExecuteReader()) { while (reader.Read()) { IDataRecord procRow = reader; string procedureName = (string)procRow["ROUTINE_NAME"]; if ((string)procRow["SQL_DATA_ACCESS"] == "CONTAINS SQL") { /* TODO: I could envision a get server time function that doesn't read or write data but should be part of a DataAccess class. */ Shared.Info("Skipping procedure " + procedureName + " marked CONTAINS SQL, as this implies it does not read or write data."); } else { int lastUnderscoreIndex = procedureName.IndexOf("_", moduleName.Length + 2); string tableName = procedureName.Substring(moduleName.Length + 1, lastUnderscoreIndex - moduleName.Length - 1); string catalogName = (string)procRow["ROUTINE_CATALOG"]; if (catalogName != "def") { throw new ApplicationException("Unexpected catalog name found: " + catalogName); } if (tableDefMap.ContainsKey(tableName) == false) { throw new ApplicationException("Table " + tableName + " referenced in stored procedure " + procedureName + " was not found in table definitions."); } TableDef tableDef = tableDefMap[tableName]; ProcedureDef procedureDef = new ProcedureDef { ProcedureName = procedureName, TableDef = tableDef, ParameterDefMap = new Dictionary <string, ParameterDef>(), FieldDefMap = new Dictionary <string, FieldDef>() /* ReadOnly, OutputsRows */ }; /* TODO: there may be some issues here if we require list and read functions to a data access level of reads sql data, i think * it may change the transaction scope if you mix with modifies sql data (not sure) */ switch ((string)procRow["SQL_DATA_ACCESS"]) { case "READS SQL DATA": procedureDef.ReadOnly = true; procedureDef.OutputsRows = true; break; case "MODIFIES SQL DATA": procedureDef.ReadOnly = false; procedureDef.OutputsRows = false; break; default: throw new ApplicationException("Unrecognized SQL Data Access setting for procedure " + procedureName + ": " + procRow["SQL_DATA_ACCESS"]); } tableDef.ProcedureDefMap[procedureName] = procedureDef; globalProcedureDefMap[procedureName] = procedureDef; } } } } PopulateParameters(databaseName, moduleName, globalProcedureDefMap); /* * call each procedure to see the result set * */ foreach (ProcedureDef procedureDef in globalProcedureDefMap.Values) { if (procedureDef.OutputsRows) { MySqlTransaction trans = this.mySqlConnection.BeginTransaction(); using (MySqlCommand command = new MySqlCommand() { CommandText = procedureDef.ProcedureName, CommandType = CommandType.StoredProcedure, Connection = this.mySqlConnection }) { foreach (ParameterDef parameterDef in procedureDef.ParameterDefMap.Values) { ParameterDirection direction; if (parameterDef.IsOutParameter) { direction = ParameterDirection.Output; } else { direction = ParameterDirection.Input; } var parameter = new MySqlParameter { ParameterName = parameterDef.ParameterName, Direction = direction, Size = parameterDef.ParameterSize, MySqlDbType = GetMySqlDbType(parameterDef.ParameterDataTypeCode) }; /* for (Parameter p : proc.parameters) { * p.sqlType.setTestValue(cs, p.procParamName); * } */ parameter.Value = DBNull.Value; command.Parameters.Add(parameter); } /* alternatively for MSSQL at least: * * SELECT COLUMN_NAME * FROM * INFORMATION_SCHEMA.COLUMNS * WHERE * TABLE_NAME = 'vwGetData' * ORDER BY * ORDINAL_POSITION ASC; * */ // cs.setMaxRows(0); DataTable tableSchema; Dictionary <string, string> fieldTypeLookup = new Dictionary <string, string>(); using (var reader = command.ExecuteReader(CommandBehavior.SchemaOnly)) { reader.Read(); tableSchema = reader.GetSchemaTable(); for (int i = 0; i < reader.FieldCount; i++) { fieldTypeLookup[reader.GetName(i)] = reader.GetDataTypeName(i); } } trans.Rollback(); foreach (DataRow row in tableSchema.Rows) { /* IsIdentity */ /* * int columnSize = (int)row["ColumnSize"]; * string sqlTypeCode = * bool isReadOnly = (bool)row["IsReadOnly"]; * bool isPrimaryKey = (bool)row["IsKey"]; * bool isAutoIncrement = (bool)row["IsAutoIncrement"]; */ string fieldName = row["ColumnName"].ToString(); string baseTableName = row["BaseTableName"].ToString(); string baseColumnName = row["BaseColumnName"].ToString(); bool isNullable = (bool)row["AllowDBNull"]; string dataTypeCode = TypeConvertor.ConvertSQLToCSharp(fieldTypeLookup[fieldName].ToLowerInvariant()); /* TODO: This check wouldn't really be necessary if we could rely on GetSchemaTable's DataType field */ if (tableDefMap.ContainsKey(baseTableName)) { if (tableDefMap[baseTableName].ColumnDefMap.ContainsKey(baseColumnName)) { string newDataTypeCode = tableDefMap[baseTableName].ColumnDefMap[baseColumnName].ColumnType; if (newDataTypeCode != dataTypeCode) { Shared.Warning(" GetTableSchema reported an incorrect data type of " + dataTypeCode + " from stored procedure " + procedureDef.ProcedureName + ", field " + fieldName + ", instead of the source table's column data type of " + newDataTypeCode + "."); dataTypeCode = newDataTypeCode; } } } FieldDef fieldDef = new FieldDef { FieldName = fieldName, ProcedureDef = procedureDef, DataTypeCode = dataTypeCode, IsNullable = isNullable, BaseTableName = baseTableName, BaseColumnName = baseColumnName }; procedureDef.FieldDefMap[fieldDef.FieldName] = fieldDef; } } } } return(globalProcedureDefMap.Values.ToList <ProcedureDef>()); }
public static List <ModelDef> CreateModelDefList(string modelNamespace, string moduleName, Dictionary <string, ModelDef> modelDefMap, List <ProcedureDef> procedureDefList, List <string> ignoreTableNameList, List <TableMapping> tableMappingList, bool cleanOracle) { // List<ModelDef> modelDefList = new List<ModelDef>(); foreach (ProcedureDef procedureDef in procedureDefList) { string procedureName = procedureDef.ProcedureName; int firstUnderscoreIndex = procedureName.IndexOf('_'); int secondUnderscoreIndex = procedureName.IndexOf('_', firstUnderscoreIndex + 1); // int lastUnderscoreIndex = procedureName.LastIndexOf('_'); // string modelName = procedureName.Substring(firstUnderscoreIndex + 1, lastUnderscoreIndex - firstUnderscoreIndex - 1); // string functionName = procedureName.Substring(lastUnderscoreIndex + 1); /* This assumes that no tables have underscores in their names */ /* TODO: probably need an table name underscore removal method elsewhere. */ string modelName = procedureName.Substring(firstUnderscoreIndex + 1, secondUnderscoreIndex - firstUnderscoreIndex - 1); string functionName = procedureName.Substring(secondUnderscoreIndex + 1); /* skip tables we are ignoring */ if (ignoreTableNameList.Contains(modelName)) { continue; } ModelDef modelDef = null; if (modelDefMap.ContainsKey(modelName)) { modelDef = modelDefMap[modelName]; } else { Shared.Info("Adding a virtual model after table named " + modelName + " from procedure " + procedureName + " which did not have a matching model in the models collection."); modelDef = new ModelDef { ModelName = modelName, FieldDefMap = new Dictionary <string, FieldDef>(), FunctionDefList = new List <FunctionDef>(), Namespace = "", PropertyDefMap = new Dictionary <string, PropertyDef>(), UsesBuildListFunction = false, UsesMakeObjectFunction = false, IsJustTable = true }; modelDefMap[modelName] = modelDef; } FunctionDef functionDef = new FunctionDef { FunctionName = functionName, ProcedureDef = procedureDef, ArgumentDefList = new List <ArgumentDef>() }; modelDef.FunctionDefList.Add(functionDef); bool isSingleRow = (functionName.StartsWith("Read")); if (procedureDef.OutputsRows == true) { functionDef.OutputsList = true; modelDef.UsesMakeObjectFunction = true; functionDef.ReturnTypeCode = modelDef.ModelName; functionDef.ReturnTypeNamespace = modelDef.Namespace; if (isSingleRow == true) { functionDef.OutputsList = false; functionDef.OutputsObject = true; } else { functionDef.OutputsList = true; functionDef.OutputsObject = false; modelDef.UsesBuildListFunction = true; } } else { functionDef.OutputsList = false; functionDef.OutputsObject = false; } foreach (ParameterDef parameterDef in procedureDef.ParameterDefMap.Values) { string typeCode = TypeConvertor.ConvertNullableSQLToCSharp(parameterDef.ParameterDataTypeCode, parameterDef.IsNullable); if (parameterDef.IsOutParameter == true) { if (functionDef.ReturnTypeCode != null) { throw new ApplicationException("Stored procedure " + procedureDef.ProcedureName + " returns row data but also has an out parameter: " + parameterDef.ParameterName); } functionDef.ReturnTypeCode = typeCode; functionDef.ReturnTypeNamespace = null; } else { bool isNullable = false; if (typeCode != "string") { if (parameterDef.ColumnDef != null) { if (parameterDef.ColumnDef.IsNullable == true) { isNullable = true; } } else { /* if we don't know, we are going to allow them */ isNullable = true; } } ArgumentDef argumentDef = new ArgumentDef { ArgumentName = Char.ToLowerInvariant(parameterDef.ParameterName[1]) + parameterDef.ParameterName.Substring(2), ArgumentTypeCode = typeCode, ParameterDef = parameterDef, IsNullable = isNullable }; parameterDef.ArgumentDef = argumentDef; string parameterName = parameterDef.ParameterName; PropertyDef propertyDef = modelDef.GetLikelyPropertyDef(parameterDef.ParameterName.Substring(1)); if (propertyDef != null) { argumentDef.PropertyDef = propertyDef; parameterDef.PropertyDef = propertyDef; // Shared.Info($"DEBUG: Found propertyDef of {propertyDef.PropertyName} for parameterName:{parameterDef.ParameterName} in function {functionName}."); /* TODO: seems like there should be a better way of storing isNullable at the property level */ /* we can't know from the models property type if strings are nullable or not so we just always assume they are */ if (propertyDef.PropertyTypeCode.EndsWith("?") == true || propertyDef.PropertyTypeCode == "string") { argumentDef.IsNullable = true; parameterDef.IsNullable = true; } } else { Shared.Info($"Warning: Could not find a propertyDef for parameterName:{parameterDef.ParameterName} in function {functionName} of {modelName}.."); } functionDef.ArgumentDefList.Add(argumentDef); } } if (procedureDef.OutputsRows == true) { functionDef.OutputPropertyDefList = new List <PropertyDef>(); foreach (FieldDef fieldDef in procedureDef.FieldDefMap.Values) { string fieldName = fieldDef.FieldName; string convertedFieldName = NameMapping.MakeCleanColumnName(tableMappingList, fieldDef.BaseTableName, modelDef.ModelName, fieldName, cleanOracle); PropertyDef propertyDef = modelDef.GetLikelyPropertyDef(convertedFieldName); /* We can easily get a field that is several layers back from our root object from joins * in the query. * for example Book.Author.City.State.Country.CountryName, and the query returns CountryName. * We need to work down through the object graph to find the model that matches the table * which that field is using. * It may be that in the initial procedure read when we query the first recordset, with a browse value that * returns join columns, that we can use that information to traverse the model tree more * directly. */ List <PropertyDef> propertyDefChain = null; if (propertyDef == null) { var referencedModelName = NameMapping.MakeCleanTableName(tableMappingList, fieldDef.BaseTableName, cleanOracle); // Shared.Info($"DEBUG:convertedFieldName:{convertedFieldName}, fieldDef.BaseTableName={fieldDef.BaseTableName}, referencedModelName={referencedModelName}"); if (modelDefMap.ContainsKey(referencedModelName) == true) { var referencedModelDef = modelDefMap[referencedModelName]; var usedModelDefList = new List <ModelDef>() { modelDef }; propertyDefChain = modelDef.ScanForLikelyPropertyDef(new List <PropertyDef>(), convertedFieldName, referencedModelDef, modelDefMap.Values.ToList <ModelDef>(), usedModelDefList); if (propertyDefChain != null) { //Shared.Info($"DEBUG: Found propertydef chain! fieldName:{convertedFieldName} in procedure {procedureDef.ProcedureName}"); //propertyDefChain.ForEach(x => { // Shared.Info($"{x.PropertyTypeNamespace}.{x.PropertyTypeCode} {x.PropertyName}"); //}); } } } /* if we didn't find a propertydef nor a propertydefchain, then it belongs as part of a function-specific Result output */ if (propertyDef == null && propertyDefChain == null) { if (functionDef.UsesResult == false) { functionDef.UsesResult = true; functionDef.ResultPropertyDefList = new List <ResultPropertyDef>(); } functionDef.ResultPropertyDefList.Add(new ResultPropertyDef { PropertyName = CleanPropertyName(convertedFieldName), PropertyTypeCode = fieldDef.DataTypeCode, FieldDef = fieldDef }); Shared.Info($"Warning: Could not find a propertyDef for fieldName \"{convertedFieldName}\" in procedure \"{procedureDef.ProcedureName}\". " + $"The base table name for this field at the SQL level was \"{fieldDef.BaseTableName}\". " + $"The converted model name was computed as \"{NameMapping.MakeCleanTableName(tableMappingList, fieldDef.BaseTableName, cleanOracle)}\". " + $"This field will be included as a property in a result class labeled \"{functionDef.FunctionName}Result\" created just for the output of this function."); } /* TODO: Commented the type check because it couldn't resolve object v. key value. May not be necessary really. * /* * * string propertyTypeCode = TypeConvertor.ConvertNullableSQLToCSharp(fieldDef.DataTypeCode, fieldDef.IsNullable); * * if (propertyDef.PropertyTypeCode != propertyTypeCode) { * throw new ApplicationException("PropertyTypeCode for " + modelDef.ModelName + "." + propertyDef.PropertyName + " found " + propertyDef.PropertyTypeCode + " but wanted " + propertyTypeCode + " based on field " + fieldDef.FieldName + " with data type " + fieldDef.DataTypeCode + " and IsNullable=" + fieldDef.IsNullable); * } */ // propertyDef.FieldDefList.Add(fieldDef); fieldDef.PropertyDef = propertyDef; fieldDef.PropertyDefChain = propertyDefChain; functionDef.OutputPropertyDefList.Add(propertyDef); if (modelDef.FieldDefMap.ContainsKey(fieldDef.FieldName)) { FieldDef foundFieldDef = modelDef.FieldDefMap[fieldDef.FieldName]; if (foundFieldDef.BaseTableName != fieldDef.BaseTableName) { throw new ApplicationException("A stored procedure (" + procedureDef.ProcedureName + ") based on model " + modelDef.ModelName + " returned a field pointing to a base table named " + fieldDef.BaseTableName + ", but another procedure (" + foundFieldDef.ProcedureDef.ProcedureName + " had produced a field with the same name based on table " + foundFieldDef.BaseTableName + ". Consider using two different names for this value in the two procedures."); } } else { modelDef.FieldDefMap[fieldDef.FieldName] = fieldDef; } } } } return(modelDefMap.Values.ToList <ModelDef>()); }
public static void MakeModels(List <TableDef> tableDefList, string namespaceText, string directory, List <TableMapping> tableMappingList, bool cleanOracle) { if (directory.EndsWith(Path.DirectorySeparatorChar.ToString(), false, CultureInfo.InvariantCulture) == false) { directory += Path.DirectorySeparatorChar; } foreach (var tableDef in tableDefList.OrderBy(x => x.TableName)) { /* TODO: For now we skip views, but should we? */ if (tableDef.TableType == "TABLE") { Shared.Info("Making model from table " + tableDef.TableName); var modelName = NameMapping.MakeCleanTableName(tableMappingList, tableDef.TableName, cleanOracle); Shared.Info($"\t\tusing model name {modelName}"); StringBuilder buildText = new StringBuilder(); buildText.AppendLine("namespace " + namespaceText + " {\n"); buildText.AppendLine("\tpublic partial class " + modelName + " {\n"); buildText.AppendLine(); bool needsSystem = false; foreach (var columnDef in tableDef.ColumnDefMap.Values) { string columnTypeCode; string columnName = columnDef.ColumnName; string convertedColumnName = NameMapping.MakeCleanColumnName(tableMappingList, tableDef.TableName, modelName, columnName, cleanOracle); if (columnDef.ReferencedTableDef != null) { if (convertedColumnName.EndsWith("Key", StringComparison.InvariantCultureIgnoreCase)) { convertedColumnName = convertedColumnName.Substring(0, convertedColumnName.Length - 3); } var rawReferencedTableName = columnDef.ReferencedTableDef.TableName; columnTypeCode = NameMapping.MakeCleanTableName(tableMappingList, rawReferencedTableName, cleanOracle); } else { columnTypeCode = TypeConvertor.ConvertSQLToCSharp(columnDef.ColumnType); if (columnTypeCode == "DateTime") { needsSystem = true; } if (columnTypeCode.Contains("[]")) { needsSystem = true; } else { if (columnDef.ForceToBit == true) { columnTypeCode = "bool"; } if (columnDef.IsNullable && columnTypeCode != "string") { columnTypeCode += "?"; } } } buildText.AppendLine("\t\tpublic " + columnTypeCode + " " + convertedColumnName + " { get; set; }"); } buildText.AppendLine("\t}"); buildText.Append("}"); if (needsSystem) { buildText.Insert(0, "using System;" + Environment.NewLine); } File.WriteAllText(directory + modelName + ".cs", buildText.ToString()); } } /* TODO: consider whether we want to make collections when our tables are referenced */ }