void GenerateProperty(CodegenOutputFile writer, Table table, Column column) { string propertyName = GetPropertyNameForDatabaseColumn(table, column.ColumnName); string privateVariable = $"_{propertyName.Substring(0, 1).ToLower()}{propertyName.Substring(1)}"; if (TrackPropertiesChange) { writer.WriteLine($"private {GetTypeDefinitionForDatabaseColumn(table, column) ?? ""} {privateVariable};"); } if (column.IsPrimaryKeyMember) { writer.WriteLine("[Key]"); } // We'll decorate [Column("Name")] only if column name doesn't match property name if (propertyName.ToLower() != column.ColumnName.ToLower()) { writer.WriteLine($"[Column(\"{column.ColumnName}\")]"); } if (TrackPropertiesChange) { writer.WriteLine($@" public {GetTypeDefinitionForDatabaseColumn(table, column) ?? ""} {propertyName} {{ get {{ return {privateVariable}; }} set {{ SetField(ref {privateVariable}, value, nameof({propertyName})); }} }}"); } else { writer.WriteLine($"public {GetTypeDefinitionForDatabaseColumn(table, column) ?? ""} {propertyName} {{ get; set; }}"); } }
void GenerateProperty(CodegenOutputFile writer, Table table, Column column) { string propertyName = GetPropertyNameForDatabaseColumn(table, column.ColumnName); if (column.IsPrimaryKeyMember) { writer.WriteLine("[Key]"); } // We'll decorate [Column("Name")] only if column name doesn't match property name if (propertyName.ToLower() != column.ColumnName.ToLower()) { writer.WriteLine($"[Column(\"{column.ColumnName}\")]"); } writer.WriteLine($"public {GetTypeDefinitionForDatabaseColumn(table, column) ?? ""} {propertyName} {{ get; set; }}"); }
void GenerateCrudExtensionsSave(CodegenOutputFile writer, Table table) { writer.WriteLine(@" /// <summary> /// Saves (if new) or Updates (if existing) /// </summary>"); writer.WithCBlock($"public static void Save(this IDbConnection conn, {GetClassNameForTable(table)} e, IDbTransaction transaction = null, int? commandTimeout = null)", () => { var pkCols = table.Columns .Where(c => ShouldProcessColumn(table, c)) .Where(c => c.IsPrimaryKeyMember).OrderBy(c => c.OrdinalPosition); writer.WriteLine($@" if ({string.Join(" && ", pkCols.Select(col => "e." + GetPropertyNameForDatabaseColumn(table, col.ColumnName) + $" == {GetDefaultValue(GetTypeForDatabaseColumn(table, col))}"))}) conn.Insert(e, transaction, commandTimeout); else conn.Update(e, transaction, commandTimeout);"); }); }
void GenerateSave(CodegenOutputFile writer, Table table) { writer.WithCBlock("public void Save()", () => { var pkCols = table.Columns .Where(c => ShouldProcessColumn(table, c)) .Where(c => c.IsPrimaryKeyMember).OrderBy(c => c.OrdinalPosition); writer.WriteLine($@" if ({string.Join(" && ", pkCols.Select(col => GetPropertyNameForDatabaseColumn(table, col.ColumnName) + $" == {GetDefaultValue(GetTypeForDatabaseColumn(table, col))}"))}) Insert(); else Update();"); }); }
void GenerateActiveRecordInsert(CodegenOutputFile writer, Table table) { writer.WithCBlock("public void Insert()", () => { writer.WithCBlock("using (var conn = IDbConnectionFactory.CreateConnection())", () => { var cols = table.Columns .Where(c => ShouldProcessColumn(table, c)) .Where(c => !c.IsIdentity) .Where(c => !c.IsRowGuid) //TODO: should be used only if they have value set (not default value) .Where(c => !c.IsComputed) //TODO: should be used only if they have value set (not default value) .OrderBy(c => GetPropertyNameForDatabaseColumn(table, c.ColumnName)); writer.WithIndent($"string cmd = @\"{Environment.NewLine}INSERT INTO {(table.TableSchema == "dbo" ? "" : $"[{table.TableSchema}].")}[{table.TableName}]{Environment.NewLine}(", ")", () => { writer.WriteLine(string.Join($",{Environment.NewLine}", cols.Select(col => $"[{col.ColumnName}]"))); });
void GenerateInsert(CodegenOutputFile writer, Table table) { //TODO: IDbConnection cn, IDbTransaction transaction = null, bool buffered = true, int? commandTimeout = default(int?), CommandType? commandType = default(CommandType?), bool logChange = true, bool logError = true writer.WithCBlock("public void Insert()", () => { writer.WithCBlock("using (var conn = IDbConnectionFactory.CreateConnection())", () => { var cols = table.Columns .Where(c => ShouldProcessColumn(table, c)) .Where(c => !c.IsIdentity) .Where(c => !c.IsRowGuid) //TODO: should be used only if they have value set (not default value) .Where(c => !c.IsComputed) //TODO: should be used only if they have value set (not default value) .OrderBy(c => GetPropertyNameForDatabaseColumn(table, c.ColumnName)); writer.WithIndent($"string cmd = @\"{Environment.NewLine}INSERT INTO {(table.TableSchema == "dbo" ? "" : $"[{table.TableSchema}].")}[{table.TableName}]{Environment.NewLine}(", ")", () => { writer.WriteLine(string.Join($",{Environment.NewLine}", cols.Select(col => $"[{col.ColumnName}]"))); });
/// <summary> /// Generates POCOS /// </summary> /// <param name="targetFolder">Absolute path of the target folder where files will be written</param> public void Generate(string targetFolder) { Console.WriteLine($"TargetFolder: {targetFolder}"); _generatorContext = new CodegenContext(); Console.WriteLine("Reading Schema..."); LogicalSchema schema = Newtonsoft.Json.JsonConvert.DeserializeObject <LogicalSchema>(File.ReadAllText(_inputJsonSchema)); //schema.Tables = schema.Tables.Select(t => Map<LogicalTable, Table>(t)).ToList<Table>(); CodegenOutputFile writer = null; if (SingleFile) { writer = _generatorContext[singleFileName]; writer .WriteLine(@"using System;") .WriteLine(@"using System.Collections.Generic;") .WriteLine(@"using System.ComponentModel.DataAnnotations;") .WriteLine(@"using System.ComponentModel.DataAnnotations.Schema;") .WriteLine(@"using System.Linq;"); if (GenerateActiveRecord) { writer.WriteLine(@"using Dapper;"); } writer .WriteLine() .WriteLine($"namespace {Namespace}").WriteLine("{").IncreaseIndent(); } if (GenerateActiveRecord) { using (var writerConnectionFactory = _generatorContext["..\\IDbConnectionFactory.cs"]) { writerConnectionFactory.WriteLine($@" using System; using System.Data; using System.Data.SqlClient; namespace {Namespace} {{ public class IDbConnectionFactory {{ public static IDbConnection CreateConnection() {{ string connectionString = @""Data Source=MYWORKSTATION\\SQLEXPRESS; Initial Catalog=AdventureWorks; Integrated Security=True;""; return new SqlConnection(connectionString); }} }} }} "); } } foreach (var table in schema.Tables.OrderBy(t => GetClassNameForTable(t))) { if (table.TableType == "VIEW") { continue; } GeneratePOCO(table); } if (SingleFile) { writer.DecreaseIndent().WriteLine("}"); // end of namespace } // since no errors happened, let's save all files _generatorContext.SaveFiles(outputFolder: targetFolder); Console.WriteLine("Success!"); }
void GeneratePOCO(Table table) { Console.WriteLine($"Generating {table.TableName}..."); CodegenOutputFile writer = null; if (SingleFile) { writer = _generatorContext[singleFileName]; } else { writer = _generatorContext[GetFileNameForTable(table)]; writer .WriteLine(@"using System;") .WriteLine(@"using System.Collections.Generic;") .WriteLine(@"using System.ComponentModel.DataAnnotations;") .WriteLine(@"using System.ComponentModel.DataAnnotations.Schema;") .WriteLine(@"using System.Linq;"); if (GenerateActiveRecord) { writer.WriteLine(@"using Dapper;"); } writer .WriteLine() .WriteLine($"namespace {Namespace}").WriteLine("{").IncreaseIndent(); } string entityClassName = GetClassNameForTable(table); // We'll decorate [Table("Name")] only if schema not default or if table name doesn't match entity name if (table.TableSchema != "dbo") //TODO or table different than clas name? { writer.WriteLine($"[Table(\"{table.TableName}\", Schema = \"{table.TableSchema}\")]"); } else if (entityClassName.ToLower() != table.TableName.ToLower()) { writer.WriteLine($"[Table(\"{table.TableName}\")]"); } writer.WithCBlock($"public partial class {entityClassName}", () => { writer.WriteLine("#region Members"); var columns = table.Columns .Where(c => ShouldProcessColumn(table, c)) .OrderBy(c => c.IsPrimaryKeyMember ? 0 : 1) .ThenBy(c => c.IsPrimaryKeyMember ? c.OrdinalPosition : 0) // respect PK order... .ThenBy(c => GetPropertyNameForDatabaseColumn(table, c.ColumnName)); // but for other columns do alphabetically; foreach (var column in columns) { GenerateProperty(writer, table, column); } writer.WriteLine("#endregion Members"); if (GenerateActiveRecord && table.TableType == "TABLE" && columns.Any(c => c.IsPrimaryKeyMember)) { writer.WriteLine(); writer.WriteLine("#region ActiveRecord"); GenerateSave(writer, table); GenerateInsert(writer, table); GenerateUpdate(writer, table); writer.WriteLine("#endregion ActiveRecord"); } if (GenerateEqualsHashCode) { writer.WriteLine(); writer.WriteLine("#region Equals/GetHashCode"); GenerateEquals(writer, table); GenerateGetHashCode(writer, table); GenerateInequalityOperatorOverloads(writer, table); writer.WriteLine("#endregion Equals/GetHashCode"); } }); if (!SingleFile) { writer.DecreaseIndent().WriteLine("}"); // end of namespace } }
/// <summary> /// Generates POCOS /// </summary> public void Generate() { while (string.IsNullOrEmpty(InputJsonSchema)) { Console.WriteLine($"[Choose an Input JSON Schema File]"); Console.Write($"Input file: "); InputJsonSchema = Console.ReadLine(); } while (string.IsNullOrEmpty(TargetFolder)) { Console.WriteLine($"[Choose a Target Folder]"); Console.Write($"Target Folder: "); TargetFolder = Console.ReadLine(); } while (string.IsNullOrEmpty(Namespace)) { Console.WriteLine($"[Choose a Namespace]"); Console.Write($"Namespace: "); Namespace = Console.ReadLine(); } _generatorContext = new CodegenContext(); Console.WriteLine("Reading Schema..."); LogicalSchema schema = Newtonsoft.Json.JsonConvert.DeserializeObject <LogicalSchema>(File.ReadAllText(InputJsonSchema)); //schema.Tables = schema.Tables.Select(t => Map<LogicalTable, Table>(t)).ToList<Table>(); CodegenOutputFile writer = null; if (SingleFile) { writer = _generatorContext[singleFileName]; writer .WriteLine(@"using System;") .WriteLine(@"using System.Collections.Generic;") .WriteLine(@"using System.ComponentModel.DataAnnotations;") .WriteLine(@"using System.ComponentModel.DataAnnotations.Schema;") .WriteLine(@"using System.Linq;"); if (GenerateActiveRecord) { writer.WriteLine(@"using Dapper;"); } if (TrackPropertiesChange) { writer.WriteLine(@"using System.ComponentModel;"); } writer .WriteLine() .WriteLine($"namespace {Namespace}").WriteLine("{").IncreaseIndent(); } if (GenerateCrudExtensions) { _dbConnectionCrudExtensions = _generatorContext[CrudExtensionsFile]; _dbConnectionCrudExtensions.Write(@" using Dapper; using System; using System.Collections.Generic; using System.Data; using System.Linq; using System.Runtime.CompilerServices; "); _dbConnectionCrudExtensions .WriteLine() .WriteLine($"namespace {Namespace}").WriteLine("{").IncreaseIndent() .WriteLine($"public static class {CrudExtensionsClass}").WriteLine("{").IncreaseIndent(); } if (GenerateActiveRecord) { using (var writerConnectionFactory = _generatorContext[ActiveRecordIDbConnectionFactoryFile]) { writerConnectionFactory.WriteLine($@" using System; using System.Data; using System.Data.SqlClient; namespace {Namespace} {{ public class IDbConnectionFactory {{ public static IDbConnection CreateConnection() {{ string connectionString = @""Data Source=MYWORKSTATION\\SQLEXPRESS; Initial Catalog=AdventureWorks; Integrated Security=True;""; return new SqlConnection(connectionString); }} }} }} "); } } foreach (var table in schema.Tables.OrderBy(t => GetClassNameForTable(t))) { if (!ShouldProcessTable(table)) { continue; } GeneratePOCO(table); } if (GenerateCrudExtensions) { _dbConnectionCrudExtensions .DecreaseIndent().WriteLine("}") // end of class .DecreaseIndent().WriteLine("}"); // end of namespace } if (SingleFile) { writer.DecreaseIndent().WriteLine("}"); // end of namespace } // since no errors happened, let's save all files _generatorContext.SaveFiles(outputFolder: TargetFolder); Console.WriteLine("Success!"); }
void GeneratePOCO(Table table) { Console.WriteLine($"Generating {table.TableName}..."); CodegenOutputFile writer = null; if (SingleFile) { writer = _generatorContext[singleFileName]; } else { writer = _generatorContext[GetFileNameForTable(table)]; writer .WriteLine(@"using System;") .WriteLine(@"using System.Collections.Generic;") .WriteLine(@"using System.ComponentModel.DataAnnotations;") .WriteLine(@"using System.ComponentModel.DataAnnotations.Schema;") .WriteLine(@"using System.Linq;"); if (GenerateActiveRecord) { writer.WriteLine(@"using Dapper;"); } if (TrackPropertiesChange) { writer.WriteLine(@"using System.ComponentModel;"); } writer .WriteLine() .WriteLine($"namespace {Namespace}").WriteLine("{").IncreaseIndent(); } string entityClassName = GetClassNameForTable(table); // We'll decorate [Table("Name")] only if schema not default or if table name doesn't match entity name if (table.TableSchema != "dbo") //TODO or table different than clas name? { writer.WriteLine($"[Table(\"{table.TableName}\", Schema = \"{table.TableSchema}\")]"); } else if (entityClassName.ToLower() != table.TableName.ToLower()) { writer.WriteLine($"[Table(\"{table.TableName}\")]"); } List <string> baseClasses = new List <string>(); if (TrackPropertiesChange) { baseClasses.Add("INotifyPropertyChanged"); } writer.WithCBlock($"public partial class {entityClassName}{(baseClasses.Any() ? " : " + string.Join(", ", baseClasses) : "")}", () => { writer.WriteLine("#region Members"); var columns = table.Columns .Where(c => ShouldProcessColumn(table, c)) .OrderBy(c => c.IsPrimaryKeyMember ? 0 : 1) .ThenBy(c => c.IsPrimaryKeyMember ? c.OrdinalPosition : 0) // respect PK order... .ThenBy(c => GetPropertyNameForDatabaseColumn(table, c.ColumnName)); // but for other columns do alphabetically; foreach (var column in columns) { GenerateProperty(writer, table, column); } writer.WriteLine("#endregion Members"); if (table.TableType == "TABLE" && columns.Any(c => c.IsPrimaryKeyMember)) { if (GenerateActiveRecord) { writer.WriteLine(); writer.WriteLine("#region ActiveRecord"); GenerateActiveRecordSave(writer, table); GenerateActiveRecordInsert(writer, table); GenerateActiveRecordUpdate(writer, table); writer.WriteLine("#endregion ActiveRecord"); } if (GenerateCrudExtensions) { _dbConnectionCrudExtensions.WriteLine(); _dbConnectionCrudExtensions.WriteLine($"#region {GetClassNameForTable(table)}"); GenerateCrudExtensionsSave(_dbConnectionCrudExtensions, table); GenerateCrudExtensionsInsert(_dbConnectionCrudExtensions, table); GenerateCrudExtensionsUpdate(_dbConnectionCrudExtensions, table); _dbConnectionCrudExtensions.WriteLine($"#endregion {GetClassNameForTable(table)}"); } } if (GenerateEqualsHashCode) { writer.WriteLine(); writer.WriteLine("#region Equals/GetHashCode"); GenerateEquals(writer, table); GenerateGetHashCode(writer, table); GenerateInequalityOperatorOverloads(writer, table); writer.WriteLine("#endregion Equals/GetHashCode"); } if (TrackPropertiesChange) { writer.WriteLine(); writer.WriteLine("#region INotifyPropertyChanged/IsDirty"); writer.WriteLine(@" public HashSet<string> ChangedProperties = new HashSet<string>(StringComparer.OrdinalIgnoreCase); public void MarkAsClean() { ChangedProperties.Clear(); } public virtual bool IsDirty => ChangedProperties.Any(); public event PropertyChangedEventHandler PropertyChanged; protected void SetField<T>(ref T field, T value, string propertyName) { if (!EqualityComparer<T>.Default.Equals(field, value)) { field = value; ChangedProperties.Add(propertyName); OnPropertyChanged(propertyName); } } protected virtual void OnPropertyChanged(string propertyName) { if (PropertyChanged != null) { PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); } }"); writer.WriteLine("#endregion INotifyPropertyChanged/IsDirty"); } }); if (!SingleFile) { writer.DecreaseIndent().WriteLine("}"); // end of namespace } }