public TableDefMap ExtractTableData(List <TableMapping> tableMappingList, bool cleanOracle) { SqlConnection conn = this.SqlConnection; // Shared.Info("begin test"); // Test(conn); // Shared.Info("Completed test"); var tableDefMap = new TableDefMap(); Shared.Info("getting tables schema"); /* * Information Schema, GetSchema differences MSSQL from MySQL: * Views data fails for MSSQL * MSSQL does not have EXTRA for columns * MySQL uses ulong for character field length, MSSQL uses int16 * MySQL uses STATISTICS for indexes * MSSQL does not indicate unique or not unique for indexes * MSSQL does not use the term "Foreign Key Collections", but "ForeignKeys" * MSSQL foreignkeys collection does not contain most fields */ /* INFORMATION_SCHEMA is kinda crap truth be told. For SQL Server, * it is just as easy to write queries for exactly what we want, * with the added benefit that it actually works. :) * * We'll try to stay consistent with the field names output to match * what information schema does, but we'll make our own queries */ string queryText = "SELECT A.name AS TableName, B.name AS SchemaName " + "FROM sys.tables AS A JOIN sys.schemas AS B " + "ON A.schema_id = B.schema_id;"; foreach (var row in MicrosoftSQL.DataAccess.GetRows(conn, queryText)) { string tableName = (string)row["TableName"]; string schemaName = (string)row["SchemaName"]; string cleanName = NameMapping.MakeCleanTableName(tableMappingList, tableName, cleanOracle); TableDef tableDef = new TableDef { TableName = tableName, CleanName = cleanName, SchemaName = schemaName, TableType = "TABLE", ColumnDefMap = new Dictionary <string, ColumnDef>(), IndexDefMap = new Dictionary <string, IndexDef>(), ProcedureDefMap = new Dictionary <string, ProcedureDef>(), FieldDefList = new List <FieldDef>(), ForeignKeyList = new List <string>(), ArgumentName = Char.ToLowerInvariant(tableName[0]) + tableName.Substring(1) }; tableDefMap[schemaName + "." + tableName] = tableDef; Shared.Info("Adding table " + tableName); } queryText = "SELECT A.name AS TableName, B.name AS SchemaName " + "FROM sys.views AS A JOIN sys.schemas AS B " + "ON A.schema_id = B.schema_id;"; foreach (var row in MicrosoftSQL.DataAccess.GetRows(conn, queryText)) { string tableName = (string)row["TableName"]; string schemaName = (string)row["SchemaName"]; string cleanName = NameMapping.MakeCleanTableName(tableMappingList, tableName, cleanOracle); TableDef tableDef = new TableDef { TableName = tableName, CleanName = cleanName, SchemaName = schemaName, TableType = "VIEW", ColumnDefMap = new Dictionary <string, ColumnDef>(), IndexDefMap = new Dictionary <string, IndexDef>(), ProcedureDefMap = new Dictionary <string, ProcedureDef>(), FieldDefList = new List <FieldDef>(), ForeignKeyList = new List <string>() }; tableDefMap[schemaName + "." + tableName] = tableDef; Shared.Info("Adding view " + tableName); } queryText = @"SELECT TABLE_NAME = ct.name, COLUMN_NAME = c.name, SCHEMA_NAME = sch.name, CHARACTER_MAXIMUM_LENGTH = c.max_length, DATA_TYPE = typ.name, IS_NULLABLE = c.is_nullable, IS_IDENTITY = is_identity FROM sys.columns c INNER JOIN sys.objects ct ON c.object_id = ct.object_id INNER JOIN sys.types typ ON c.system_type_id = typ.system_type_id INNER JOIN sys.schemas sch ON ct.schema_id = sch.schema_id WHERE ct.type IN ('U', 'V') ORDER BY TABLE_NAME, COLUMN_NAME;"; /* for MySQL: queryText = "SELECT COLUMN_NAME, TABLE_NAME, CHARACTER_MAXIMUM_LENGTH, DATA_TYPE, EXTRA, IS_NULLABLE FROM INFORMATION_SCHEMA.COLUMNS ORDER BY TABLE_NAME ASC, ORDINAL_POSITION ASC;"; */ foreach (var row in MicrosoftSQL.DataAccess.GetRows(conn, queryText)) { string columnName = (string)row["COLUMN_NAME"]; string tableName = (string)row["TABLE_NAME"]; string schemaName = (string)row["SCHEMA_NAME"]; Shared.Info("Adding column " + columnName + " from table " + tableName); short?dataLength; if (row["CHARACTER_MAXIMUM_LENGTH"] == DBNull.Value) { dataLength = null; } else { dataLength = (short)row["CHARACTER_MAXIMUM_LENGTH"]; } string modelName = NameMapping.MakeCleanTableName(tableMappingList, tableName, cleanOracle); string cleanName = NameMapping.MakeCleanColumnName(tableMappingList, tableName, modelName, columnName, cleanOracle); bool forceToBit = false; if (cleanOracle == true) { forceToBit = IsForceToBit(tableMappingList, tableName, columnName); } /* MySQL provides IsIdentity = ((string)row["EXTRA"]).Contains("auto_increment"), */ ColumnDef columnDef = new ColumnDef { ColumnName = columnName, CleanName = cleanName, ColumnType = (string)row["DATA_TYPE"], DataLength = (ulong?)dataLength, ForceToBit = forceToBit, IsIdentity = (bool)row["IS_IDENTITY"], IsNullable = (bool)row["IS_NULLABLE"] }; Shared.Info(tableName); tableDefMap[schemaName + "." + tableName].ColumnDefMap[columnName] = columnDef; Shared.Info("Column " + columnName + " added."); } /* will get primary key and unique key, which is used for Read and ReadFor functions */ /* will get indexes for use in ListFor functions */ /* had QUOTENAME function for SCHEMA_NAME and TABLE_NAME which I removed, * also removed SCHEMA_NAME = OBJECT_SCHEMA_NAME(i.[object_id]), */ queryText = @"SELECT TABLE_NAME = ct.name, SCHEMA_NAME = sch.name, INDEX_NAME = i.name, PRIMARY_KEY = i.is_primary_key, [UNIQUE] = i.is_unique, COLUMN_NAME = c.Name FROM sys.indexes AS i INNER JOIN sys.index_columns AS ic ON i.[object_id] = ic.[object_id] AND i.index_id = ic.index_id INNER JOIN sys.columns c ON ic.column_id = c.column_id AND ic.[object_id] = c.[object_id] INNER JOIN sys.objects ct ON i.object_id = ct.object_id INNER JOIN sys.schemas sch ON ct.schema_id = sch.schema_id WHERE ct.type IN ('U', 'V') ORDER BY TABLE_NAME, INDEX_NAME, ic.index_column_id;"; foreach (var row in MicrosoftSQL.DataAccess.GetRows(conn, queryText)) { string tableName = (string)row["TABLE_NAME"]; string schemaName = (string)row["SCHEMA_NAME"]; string indexName = (string)row["INDEX_NAME"]; string columnName = (string)row["COLUMN_NAME"]; Shared.Info("Adding index column " + columnName + " from index " + indexName + " on table " + tableName); // int ordinalPosition = (int)row["ORDINAL_POSITION"]; /* SORT_ORDER */ Shared.Info("looking for table " + tableName); TableDef table = tableDefMap[schemaName + "." + tableName]; IndexDef indexDef = null; if (table.IndexDefMap.TryGetValue(indexName, out indexDef) == false) { indexDef = new IndexDef { IndexName = indexName, // IsPrimary = (indexName == "PRIMARY"), IsPrimary = (bool)row["PRIMARY_KEY"], // IsUnique = ((bool)row["NON_UNIQUE"]!=true), IsUnique = (bool)row["UNIQUE"], ColumnDefList = new List <ColumnDef>() }; table.IndexDefMap[indexName] = indexDef; } ColumnDef columnDef = table.ColumnDefMap[columnName]; indexDef.ColumnDefList.Add(columnDef); } queryText = @"SELECT TABLE_NAME = ct.name, SCHEMA_NAME = sch.name, COLUMN_NAME = c.name, REFERENCED_TABLE_NAME = rct.name, REFERENCED_COLUMN_NAME = rc.name, REFERENCED_SCHEMA_NAME = refsch.name FROM sys.foreign_key_columns AS fkc INNER JOIN sys.columns c ON fkc.parent_object_id = c.object_id AND fkc.parent_column_id = c.column_id INNER JOIN sys.objects ct ON c.object_id = ct.object_id INNER JOIN sys.columns rc ON fkc.referenced_object_id = rc.object_id AND fkc.referenced_column_id = rc.column_id INNER JOIN sys.objects rct ON rc.object_id = rct.object_id INNER JOIN sys.schemas sch ON ct.schema_id = sch.schema_id INNER JOIN sys.schemas refsch ON rct.schema_id = refsch.schema_id WHERE ct.type IN ('U', 'V') ORDER BY TABLE_NAME, COLUMN_NAME;"; foreach (var row in MicrosoftSQL.DataAccess.GetRows(conn, queryText)) { TableDef tableDef = tableDefMap[row["SCHEMA_NAME"] + "." + row["TABLE_NAME"].ToString()]; ColumnDef columnDef = tableDef.ColumnDefMap[row["COLUMN_NAME"].ToString()]; columnDef.ReferencedTableDef = tableDefMap[row["REFERENCED_SCHEMA_NAME"].ToString() + "." + row["REFERENCED_TABLE_NAME"].ToString()]; columnDef.ReferencedColumnDef = columnDef.ReferencedTableDef.ColumnDefMap[row["REFERENCED_COLUMN_NAME"].ToString()]; Shared.Info("Adding foreign key for " + tableDef.TableName + "." + columnDef.ColumnName + " to " + columnDef.ReferencedTableDef.TableName + "." + columnDef.ReferencedColumnDef.ColumnName); } return(tableDefMap); }
public List <ProcedureDef> MakeProcedureDefList(string databaseName, string moduleName, Dictionary <string, TableDef> tableDefMap) { /* var tableMap = {}; * var functionMap = {}; * var tableArray = []; * * */ SqlConnection conn = this.SqlConnection; var globalProcedureDefMap = new Dictionary <string, ProcedureDef>(); using (SqlCommand command = new SqlCommand() { CommandText = $@"SELECT sobj.name as Name, (SELECT count(is_selected) FROM sys.sql_dependencies AS sis WHERE sobj.object_id = sis.object_id AND is_selected = 1) AS PerformsSelectCount, (SELECT count(is_updated) FROM sys.sql_dependencies AS siu WHERE sobj.object_id = siu.object_id AND is_updated = 1) AS PerformsUpdateCount FROM sys.objects AS sobj WHERE type = 'P' AND name like '{moduleName}\_%\_%' ESCAPE '\' ORDER BY name;", CommandType = CommandType.Text, Connection = conn }) { using (var reader = command.ExecuteReader()) { while (reader.Read()) { IDataRecord procRow = reader; string procedureName = (string)procRow["Name"]; Shared.Info("Reading procedure " + procedureName); /* we also have the ability to check referenced tables if need be */ int secondUnderscoreIndex = procedureName.IndexOf("_", moduleName.Length + 2, StringComparison.InvariantCulture); string functionalName = procedureName.Substring(moduleName.Length + 1, secondUnderscoreIndex - moduleName.Length - 1); Shared.Info($"moduleName:{moduleName}, functionalName:{functionalName}, secondUnderscoreIndex:{secondUnderscoreIndex}"); /* that is the functional name, so we want to find by that */ var tableDef = tableDefMap.Values.SingleOrDefault(x => NameMapping.GetFunctionalName(x) == functionalName); if (tableDef == null) { throw new ApplicationException("Table with functional name " + functionalName + " referenced in stored procedure " + procedureName + " was not found in table definitions."); } ProcedureDef procedureDef = new ProcedureDef { ProcedureName = procedureName, TableDef = tableDef, ParameterDefMap = new Dictionary <string, ParameterDef>(), FieldDefMap = new Dictionary <string, FieldDef>() }; Shared.Info($"procedure {procedureName}, select:{(int)procRow["PerformsSelectCount"]}, update:{(int)procRow["PerformsUpdateCount"]} "); /* DELETE and UPDATE queries will have a value in PerformsSelectCount, so we screen by PerformsUpdateCount first */ if ((int)procRow["PerformsUpdateCount"] > 0) { procedureDef.ReadOnly = false; procedureDef.OutputsRows = false; } else { procedureDef.ReadOnly = true; procedureDef.OutputsRows = true; if ((int)procRow["PerformsSelectCount"] == 0) { throw new ApplicationException($"Procedure {procedureName} had 0 values for select and for update dependency counts"); } } tableDef.ProcedureDefMap[procedureName] = procedureDef; globalProcedureDefMap[procedureName] = procedureDef; } } } PopulateParameters(databaseName, moduleName, globalProcedureDefMap); foreach (ProcedureDef procedureDef in globalProcedureDefMap.Values) { if (procedureDef.OutputsRows) { // Shared.Info("Collecting output fields from procedure " + procedureDef.ProcedureName); // Shared.Info(procedureDef.ToJSONString()); /* MSSQL 2012 and beyond have result set function, which we'll use in this version */ // PopulateFields2008(procedureDef); PopulateFields2012(procedureDef); } } 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>()); }
private string GetFunctionalName(TableDef tableDef) { return(NameMapping.GetFunctionalName(tableDef)); }
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 */ }