public Column(string name, DataTypeToken dataType, ValueToken defaultValue, bool isPrimaryKey, bool autogeneratePrimaryKey, bool isNullable, ForeignKeyDetailsToken foreignKey, int?maxLength, bool isUnique) { if (foreignKey != null && isPrimaryKey) { throw new ArgumentException($"A column cannot be both a primary and foreign key. Column: {Name}."); } Name = name?.ToPostgresStyle() ?? throw new ArgumentNullException(nameof(name)); CSharpStyleName = ColumnPlaceholder.GetCSharpName(name); CSharpStyleNameLower = ColumnPlaceholder.GetCSharpName(name, true); DataType = dataType ?? throw new ArgumentNullException(nameof(dataType)); DefaultValue = defaultValue; IsPrimaryKey = isPrimaryKey; AutogeneratePrimaryKey = autogeneratePrimaryKey; IsNullable = isNullable; ForeignKey = foreignKey; MaxLength = maxLength; IsUnique = isUnique; }
private static IReadOnlyList <Table> Parse(string input, GeneratorOptions options) { var expectsTable = true; var nextMustOpenTable = false; var careAboutSplitter = false; var precedingToken = default(IElephantToken); var tables = new List <Table>(); var columns = new List <ColumnPlaceholder>(); var currentTable = default(TablePlaceholder); var currentColumn = default(ColumnPlaceholder); foreach (var token in Tokenizer.Tokenize(input)) { if (nextMustOpenTable) { if (token == OpenTableToken.Value) { nextMustOpenTable = false; } else if (token != NewLineToken.Value) { throw new InvalidOperationException($"Expected open table token '{{' after table name: {currentTable.Name}."); } continue; } if (precedingToken == ForeignKeyToken.Value) { if (currentColumn == null) { throw new InvalidOperationException("Protected keyword 'fk' used outside column context."); } if (token is ForeignKeyDetailsToken foreignTable) { currentColumn.ForeignKey = foreignTable; } else { throw new InvalidOperationException($"No table-column of the format 'table.column' provided for foreign key. Instead found: {token}."); } precedingToken = token; continue; } if (precedingToken == DefaultToken.Value) { if (currentColumn == null) { throw new InvalidOperationException("Protected keyword 'df' used outside column context."); } if (token is ValueToken valueToken) { currentColumn.DefaultValue = valueToken; } else { throw new InvalidOperationException($"No default value of the format '[ some_value ]' provided for default. Instead found: {token}."); } precedingToken = token; continue; } switch (token) { case NewLineToken _: case CommaToken _: if (!careAboutSplitter || currentTable == null) { continue; } columns.Add(currentColumn); if (currentColumn.Name == null || currentColumn.DataType == null) { throw new InvalidOperationException("Column did not contain at least a name and a data type."); } currentColumn = null; careAboutSplitter = false; break; case NameToken name: if (expectsTable) { currentTable = new TablePlaceholder(name.Name); expectsTable = false; nextMustOpenTable = true; break; } if (currentColumn == null) { currentColumn = new ColumnPlaceholder { Name = name.Name }; careAboutSplitter = true; } break; case ColumnTableReferenceToken columnTableReferenceToken: if (expectsTable) { currentTable = new TablePlaceholder(columnTableReferenceToken.Column, columnTableReferenceToken.Table); expectsTable = false; nextMustOpenTable = true; } else { throw new InvalidOperationException($"Table-column name {columnTableReferenceToken} found outside the context of a foreign key in table: '{currentTable.Name}'."); } break; case DataTypeToken dataTypeToken: if (currentColumn == null) { throw new InvalidOperationException($"Unexpected data type declaration outside column: {dataTypeToken.Type.Name}."); } currentColumn.DataType = dataTypeToken; break; case PrimaryKeyToken _: if (currentColumn == null) { throw new InvalidOperationException($"Unexpected primary key 'pk' declaration outside column in table: '{currentTable.Name}'."); } currentColumn.IsPrimaryKey = true; break; case ForeignKeyToken _: if (currentColumn == null) { throw new InvalidOperationException($"Unexpected foreign key 'fk' declaration outside column in table: '{currentTable.Name}'."); } if (currentColumn.IsPrimaryKey) { throw new InvalidOperationException($"The column '{currentColumn.Name}' is already a primary key, it cannot also be a foreign key in table: '{currentTable.Name}'."); } break; case NullToken _: if (currentColumn == null) { throw new InvalidOperationException($"Unexpected not null 'nn' declaration outside column in table: '{currentTable.Name}'."); } if (currentColumn.IsPrimaryKey) { throw new InvalidOperationException($"The column '{currentColumn.Name}' is a primary key, it cannot be nullable."); } currentColumn.IsNullable = true; break; case ValueToken innerValueToken: if (currentColumn == null) { throw new InvalidOperationException($"Unexpected value declaration '{innerValueToken.Value}' outside column."); } if (currentColumn.DataType.Type == typeof(string) && int.TryParse(innerValueToken.Value, out var val)) { currentColumn.MaxLength = val; } else { throw new InvalidOperationException($"Unexpected value declaration '{innerValueToken.Value}' for non string column and not following default in table: '{currentTable.Name}'."); } break; case UniqueToken _: if (currentColumn == null) { throw new InvalidOperationException($"Unexpected unique 'uq' declaration outside column in table: '{currentTable.Name}'."); } currentColumn.IsUnique = true; break; case AutoGenerateToken _: if (currentColumn == null) { throw new InvalidOperationException($"Unexpected auto generate 'ag' declaration outside column in table: '{currentTable.Name}'."); } if (!currentColumn.IsPrimaryKey) { throw new InvalidOperationException($"The column '{currentColumn.Name}' is not a primary key, it cannot be autogenerated."); } currentColumn.AutogeneratePrimaryKey = true; break; case CloseTableToken _: if (currentTable == null) { throw new InvalidOperationException("Found end of table marker '}' outside of a table."); } if (currentColumn != null) { columns.Add(currentColumn); currentColumn = null; } var qualifiedName = new SchemaQualifiedName(currentTable.Schema ?? options.DefaultSchema ?? "public", currentTable.Name); var actualColumns = columns.Select(x => new Column(x.Name, x.DataType, x.DefaultValue, x.IsPrimaryKey, x.AutogeneratePrimaryKey, x.IsNullable, x.ForeignKey, x.MaxLength, x.IsUnique)); tables.Add(new Table(qualifiedName, actualColumns)); currentTable = null; columns.Clear(); expectsTable = true; break; } precedingToken = token; } return(tables); }
public IReadOnlyList <GeneratedRepository> GetRepositories(IReadOnlyList <GeneratedClass> classes, GeneratorOptions options) { var builder = new StringBuilder(); var result = new List <GeneratedRepository>(); var connectionUsing = "using (var connection = new NpgsqlConnection(" + (options.UseUnderscore ? "_connectionString" : "connectionString") + "))"; var namespaceRequired = options.ClassNamespace != options.RepositoryNamespace; foreach (var generatedClass in classes) { var name = GetQualifiedClassName(generatedClass); foreach (var requiredNamespace in RequiredNamespaces) { builder.Append("using ").Append(requiredNamespace).AppendLine(";"); } if (namespaceRequired) { builder.Append("using ").Append(options.ClassNamespace).AppendLine(";"); } builder.AppendLine(); builder.Append("namespace ").AppendLine(options.RepositoryNamespace).AppendLine("{"); // Now inside the namespace { builder.AddTab(options).Append("public class ").Append(generatedClass.Name).Append(options.RepositorySuffix).AppendLine(); builder.AddTab(options).AppendLine("{"); // Now inside the class { AddConstructorAndField(generatedClass, options, builder); builder.AppendLine(); // ReSharper disable UnusedVariable using (var getAll = new GeneratedMethod($"IEnumerable<{name}>", GetAll, string.Empty, builder, options)) { using (var conn = new GeneratedUsing(connectionUsing, builder, options)) { builder.AddTab(options, 4) .Append("var command = new NpgsqlCommand(@\""); var statement = generatedClass.Sql.GetSelectAll(); AddWithAlignedIndentation(statement, 4, options, builder); builder.AppendLine("\", connection);").AppendLine(); using (var reader = new GeneratedUsing("using (var reader = await command.ExecuteReaderAsync())", builder, options, 4)) { builder.AddTab(options, 5).AppendLine("return Enumerate(reader);"); } } } builder.AppendLine(); var pkCol = generatedClass.Table.GetPrimaryKey(); var pkParamName = pkCol.CSharpStyleNameLower; var argByPk = pkCol.DataType.CSharpType + " " + pkParamName; using (var get = new GeneratedMethod(name, Get, argByPk, builder, options)) { using (var conn = new GeneratedUsing(connectionUsing, builder, options)) { builder.AddTab(options, 4) .Append("var command = new NpgsqlCommand(@\""); var statement = generatedClass.Sql.GetSelectByPk(); AddWithAlignedIndentation(statement, 4, options, builder); builder.AppendLine("\", connection);").AppendLine(); builder.AddTab(options, 4).Append("command.Parameters.AddWithValue(\"") .Append(pkParamName) .Append("\", ").Append(pkParamName).AppendLine(");").AppendLine(); using (var reader = new GeneratedUsing("using (var reader = await command.ExecuteReaderAsync())", builder, options, 4)) { using (var mvNext = new GeneratedUsing("while (await reader.ReadAsync())", builder, options, 5)) { builder.AddTab(options, 6).AppendLine("return GetCurrent(reader);"); } } builder.AppendLine(); builder.AddTab(options, 4).AppendLine("return null;"); } } builder.AppendLine(); using (var del = new GeneratedMethod("bool", Delete, argByPk, builder, options)) { using (var conn = new GeneratedUsing(connectionUsing, builder, options)) { builder.AddTab(options, 4) .Append("var command = new NpgsqlCommand(@\""); var statement = generatedClass.Sql.GetDeleteByPk(); AddWithAlignedIndentation(statement, 4, options, builder); builder.AppendLine("\", connection);").AppendLine(); builder.AddTab(options, 4).Append("command.Parameters.AddWithValue(\"") .Append(pkParamName) .Append("\", ").Append(pkParamName).AppendLine(");"); builder.AppendLine().AddTab(options, 4).AppendLine("var result = await command.ExecuteNonQueryAsync();"); builder.AppendLine().AddTab(options, 4).AppendLine("return result > 0;"); } } builder.AppendLine(); var paramName = ColumnPlaceholder.GetCSharpName(generatedClass.Name, true); using (var create = new GeneratedMethod(name, Create, $"{name} {paramName}", builder, options)) { using (var conn = new GeneratedUsing(connectionUsing, builder, options)) { builder.AddTab(options, 4) .Append("var command = new NpgsqlCommand(@\""); var statement = generatedClass.Sql.Create(options); AddWithAlignedIndentation(statement.sql, 4, options, builder); builder.AppendLine("\", connection);").AppendLine(); foreach (var param in statement.parameters) { builder.AddTab(options, 4).Append("command.Parameters.AddWithValue(\"") .Append(param) .Append("\", ").Append($"{paramName}.{ColumnPlaceholder.GetCSharpName(param)}").AppendLine(");"); } builder.AppendLine(); using (var reader = new GeneratedUsing("using (var reader = await command.ExecuteReaderAsync())", builder, options, 4)) { using (var mvNext = new GeneratedUsing("while (await reader.ReadAsync())", builder, options, 5)) { builder.AddTab(options, 6).AppendLine("return GetCurrent(reader);"); } } builder.AppendLine(); builder.AddTab(options, 4).AppendLine("return null;"); } } builder.AppendLine(); using (var update = new GeneratedMethod(name, Update, $"{name} {paramName}", builder, options)) { using (var conn = new GeneratedUsing(connectionUsing, builder, options)) { builder.AddTab(options, 4) .Append("var command = new NpgsqlCommand(@\""); var statement = generatedClass.Sql.Update(options); AddWithAlignedIndentation(statement.sql, 4, options, builder); builder.AppendLine("\", connection);").AppendLine(); foreach (var param in statement.parameters) { builder.AddTab(options, 4).Append("command.Parameters.AddWithValue(\"") .Append(param) .Append("\", ").Append($"{paramName}.{ColumnPlaceholder.GetCSharpName(param)}").AppendLine(");"); } builder.AppendLine(); using (var reader = new GeneratedUsing("using (var reader = await command.ExecuteReaderAsync())", builder, options, 4)) { using (var mvNext = new GeneratedUsing("while (await reader.ReadAsync())", builder, options, 5)) { builder.AddTab(options, 6).AppendLine("return GetCurrent(reader);"); } } builder.AppendLine(); builder.AddTab(options, 4).AppendLine("return null;"); } } builder.AppendLine(); using (var enumer = new GeneratedMethod($"IEnumerable<{name}>", "Enumerate", "DbDataReader reader", builder, options, "private", false, true)) { using (var reader = new GeneratedUsing("while (reader.Read())", builder, options)) { builder.AddTab(options, 4).AppendLine("yield return GetCurrent(reader);"); } } builder.AppendLine(); using (var enumer = new GeneratedMethod(name, "GetCurrent", "DbDataReader reader", builder, options, "private", false, true)) { builder.AddTab(options, 3).Append("return new ").Append(name).AppendLine() .AddTab(options, 3).AppendLine("{"); for (var i = 0; i < generatedClass.Table.Columns.Count; i++) { var col = generatedClass.Table.Columns[i]; var colName = ColumnPlaceholder.GetCSharpName(col.Name); builder.AddTab(options, 4).Append(colName).Append(" = ") .Append(GetReaderQueryForColumn(col, i)); if (i < generatedClass.Table.Columns.Count - 1) { builder.AppendLine(","); } else { builder.AppendLine(); } } builder.AddTab(options, 3).AppendLine("};"); } } // ReSharper restore UnusedVariable builder.AddTab(options).AppendLine("}"); } builder.Append('}'); var repoCode = builder.ToString(); result.Add(new GeneratedRepository(generatedClass.Table, repoCode, options.RepositoryNamespace)); builder.Clear(); } return(result); }