protected virtual void WriteClass(CodeWriter writer, Table table, Database schema, GenerationContext context) { writer.WriteLine(); string entityBase = context.Parameters.EntityBase; if (string.IsNullOrEmpty(entityBase)) entityBase = schema.EntityBase; var specifications = SpecificationDefinition.Partial; if (table.Type.AccessModifierSpecified) specifications |= GetSpecificationDefinition(table.Type.AccessModifier); else specifications |= SpecificationDefinition.Public; if (table.Type.ModifierSpecified) specifications |= GetSpecificationDefinition(table.Type.Modifier); var tableAttribute = NewAttributeDefinition<TableAttribute>(); tableAttribute["Name"] = table.Name; using (writer.WriteAttribute(tableAttribute)) using (writer.WriteClass(specifications, table.Type.Name, entityBase, context.Parameters.EntityInterfaces)) { WriteClassHeader(writer, table, context); WriteCustomTypes(writer, table, schema, context); WriteClassExtensibilityDeclarations(writer, table, context); WriteClassProperties(writer, table, context); if (context.Parameters.GenerateEqualsHash) WriteClassEqualsAndHash(writer, table, context); WriteClassChildren(writer, table, schema, context); WriteClassParents(writer, table, schema, context); WriteClassChildrenAttachment(writer, table, schema, context); WriteClassCtor(writer, table, schema, context); } }
protected virtual void WriteDataContextProcedures(CodeWriter writer, DbLinq.Schema.Dbml.Database schema, GenerationContext context) { foreach (var procedure in schema.Functions) { WriteDataContextProcedure(writer, procedure, context); } }
protected override void LoadConstraints(Database schema, SchemaName schemaName, IDbConnection conn, NameFormat nameFormat, Names names) { var constraints = ReadConstraints(conn, schemaName.DbName); //sort tables - parents first (this is moving to SchemaPostprocess) //TableSorter.Sort(tables, constraints); foreach (DataConstraint keyColRow in constraints) { //find my table: string fullKeyDbName = GetFullDbName(keyColRow.TableName, keyColRow.TableSchema); DbLinq.Schema.Dbml.Table table = schema.Tables.FirstOrDefault(t => fullKeyDbName == t.Name); if (table == null) { bool ignoreCase = true; table = schema.Tables.FirstOrDefault(t => 0 == string.Compare(fullKeyDbName, t.Name, ignoreCase)); if (table == null) { WriteErrorLine("ERROR L46: Table '" + keyColRow.TableName + "' not found for column " + keyColRow.ColumnName); continue; } } bool isForeignKey = keyColRow.ConstraintName != "PRIMARY" && keyColRow.ReferencedTableName != null; if (isForeignKey) { LoadForeignKey(schema, table, keyColRow.ColumnName, keyColRow.TableName, keyColRow.TableSchema, keyColRow.ReferencedColumnName, keyColRow.ReferencedTableName, keyColRow.ReferencedTableSchema, keyColRow.ConstraintName, nameFormat, names); } } }
protected virtual void CheckNames(Database schema) { CheckNames(schema, column => "Contents", association => association.ThisKey + association.Member, association => association.Member + association.Type); }
protected virtual void WriteDataContextCtors(CodeWriter writer, Database schema, Type contextBaseType, GenerationContext context) { // ctor taking a connections tring WriteDataContextCtor(writer, schema, contextBaseType, new[] { new ParameterDefinition { Name = "connectionString", Type = typeof(string) } }, new[] { "connectionString" }, new[] { typeof(string) }, context); // the two constructors below have the same prototype, so they are mutually exclusive // the base class requires a IVendor if (!WriteDataContextCtor(writer, schema, contextBaseType, new[] { new ParameterDefinition { Name = "connection", Type = typeof(IDbConnection) } }, new[] { "connection", writer.GetNewExpression(writer.GetMethodCallExpression(writer.GetLiteralFullType(context.SchemaLoader.Vendor.GetType()))) }, new[] { typeof(IDbConnection), typeof(IVendor) }, context)) { // OR the base class requires no IVendor WriteDataContextCtor(writer, schema, contextBaseType, new[] { new ParameterDefinition { Name = "connection", Type = typeof(IDbConnection) } }, new[] { "connection" }, new[] { typeof(IDbConnection) }, context); } // ctor(string, MappingSource) WriteDataContextCtor(writer, schema, contextBaseType, new[] { new ParameterDefinition { Name = "connection", Type = typeof(string) }, new ParameterDefinition { Name = "mappingSource", Type = typeof(MappingSource) }, }, new[] { "connection", "mappingSource" }, new[] { typeof(string), typeof (MappingSource) }, context); // ctor(IDbConnection, MappingSource) WriteDataContextCtor(writer, schema, contextBaseType, new[] { new ParameterDefinition { Name = "connection", Type = typeof(IDbConnection) }, new ParameterDefinition { Name = "mappingSource", Type = typeof(MappingSource) }, }, new[] { "connection", "mappingSource" }, new[] { typeof(IDbConnection), typeof(MappingSource) }, context); // just in case you'd like to specify another vendor than the one who helped generating this file WriteDataContextCtor(writer, schema, contextBaseType, new[] { new ParameterDefinition { Name = "connection", Type = typeof(IDbConnection) }, new ParameterDefinition { Name = "vendor", Type = typeof(IVendor) }, }, new[] { "connection", "vendor" }, new[] { typeof(IDbConnection), typeof(IVendor) }, context); WriteDataContextCtor(writer, schema, contextBaseType, new[] { new ParameterDefinition { Name = "connection", Type = typeof(IDbConnection) }, new ParameterDefinition { Name = "mappingSource", Type = typeof(MappingSource) }, new ParameterDefinition { Name = "vendor", Type = typeof(IVendor) }, }, new[] { "connection", "mappingSource", "vendor" }, new[] { typeof(IDbConnection), typeof(MappingSource), typeof(IVendor) }, context); }
/// <summary> /// Generates a Visual Basic source code wrapper for the database schema objects. /// </summary> /// <param name="database"></param> /// <param name="filename"></param> public void GenerateVisualBasic(Database database, string filename) { using (Stream stream = File.Open(filename, FileMode.Create)) { using (StreamWriter writer = new StreamWriter(stream)) { new VBCodeProvider().CreateGenerator(writer).GenerateCodeFromNamespace(GenerateCodeDomModel(database), writer, new CodeGeneratorOptions() { BracingStyle = "C" }); } } }
public void Write(TextWriter textWriter, Database dbSchema, GenerationContext context) { Context = context; Provider.CreateGenerator(textWriter).GenerateCodeFromNamespace( GenerateCodeDomModel(dbSchema), textWriter, new CodeGeneratorOptions() { BracingStyle = "C", IndentString = "\t", }); }
protected override void LoadConstraints(Database schema, SchemaName schemaName, IDbConnection conn, NameFormat nameFormat, Names names) { //TableSorter.Sort(tables, constraints); //sort tables - parents first var constraints = ReadConstraints(conn, schemaName.DbName); var allKeys2 = ReadForeignConstraints(conn, schemaName.DbName); var foreignKeys = allKeys2.Where(k => k.ConstraintType == "FOREIGN KEY").ToList(); var primaryKeys = allKeys2.Where(k => k.ConstraintType == "PRIMARY KEY").ToList(); foreach (DataConstraint keyColRow in constraints) { //find my table: string constraintFullDbName = GetFullDbName(keyColRow.TableName, keyColRow.TableSchema); DbLinq.Schema.Dbml.Table table = schema.Tables.FirstOrDefault(t => constraintFullDbName == t.Name); if (table == null) { WriteErrorLine("ERROR L138: Table '" + keyColRow.TableName + "' not found for column " + keyColRow.ColumnName); continue; } //todo: must understand better how PKEYs are encoded. //In Sasha's DB, they don't end with "_pkey", you need to rely on ReadForeignConstraints(). //In Northwind, they do end with "_pkey". bool isPrimaryKey = keyColRow.ConstraintName.EndsWith("_pkey") || primaryKeys.Count(k => k.ConstraintName == keyColRow.ConstraintName) == 1; if (isPrimaryKey) { //A) add primary key DbLinq.Schema.Dbml.Column primaryKeyCol = table.Type.Columns.First(c => c.Name == keyColRow.ColumnName); primaryKeyCol.IsPrimaryKey = true; } else { DataForeignConstraint dataForeignConstraint = foreignKeys.FirstOrDefault(f => f.ConstraintName == keyColRow.ConstraintName); if (dataForeignConstraint == null) { string msg = "Missing data from 'constraint_column_usage' for foreign key " + keyColRow.ConstraintName; WriteErrorLine(msg); //throw new ApplicationException(msg); continue; //as per Andrus, do not throw. //putting together an Adnrus_DB test case. } LoadForeignKey(schema, table, keyColRow.ColumnName, keyColRow.TableName, keyColRow.TableSchema, dataForeignConstraint.ColumnName, dataForeignConstraint.ReferencedTableName, dataForeignConstraint.ReferencedTableSchema, keyColRow.ConstraintName, nameFormat, names); } } }
protected virtual void WriteClasses(CodeWriter writer, Database schema, GenerationContext context) { IEnumerable<Table> tables = schema.Tables; var types = context.Parameters.GenerateTypes; if (types.Count > 0) tables = tables.Where(t => types.Contains(t.Type.Name)); foreach (var table in tables) WriteClass(writer, table, schema, context); }
/// <summary> /// Loads the foreign key. /// </summary> /// <param name="schema">The schema.</param> /// <param name="table">The table.</param> /// <param name="columnName">Name of the column.</param> /// <param name="tableName">Name of the table.</param> /// <param name="tableSchema">The table schema.</param> /// <param name="referencedColumnName">Name of the referenced column.</param> /// <param name="referencedTableName">Name of the referenced table.</param> /// <param name="referencedTableSchema">The referenced table schema.</param> /// <param name="constraintName">Name of the constraint.</param> /// <param name="nameFormat">The name format.</param> /// <param name="names">The names.</param> protected virtual void LoadForeignKey(Database schema, Table table, string columnName, string tableName, string tableSchema, string referencedColumnName, string referencedTableName, string referencedTableSchema, string constraintName, NameFormat nameFormat, Names names) { var foreignKey = BuildForeignKey(names.ColumnsNames[tableName], columnName); var reverseForeignKey = BuildForeignKey(names.ColumnsNames[referencedTableName], referencedColumnName); var associationName = CreateAssociationName(tableName, tableSchema, referencedTableName, referencedTableSchema, constraintName, foreignKey, nameFormat); //both parent and child table get an [Association] var assoc = new Association(); assoc.IsForeignKey = true; assoc.Name = constraintName; assoc.Type = null; assoc.ThisKey = foreignKey; assoc.OtherKey = reverseForeignKey; assoc.Member = associationName.ManyToOneMemberName; assoc.CardinalitySpecified = false; // TODO: generate assoc.Cardinality? table.Type.Associations.Add(assoc); //and insert the reverse association: var reverseAssociation = new Association(); reverseAssociation.Name = constraintName; reverseAssociation.Type = table.Type.Name; reverseAssociation.Member = associationName.OneToManyMemberName; reverseAssociation.CardinalitySpecified = false; // TODO: generate reverseAssociation.Cardinality? reverseAssociation.ThisKey = reverseForeignKey; reverseAssociation.OtherKey = foreignKey; reverseAssociation.DeleteRule = "NO ACTION"; string referencedFullDbName = GetFullDbName(referencedTableName, referencedTableSchema); var referencedTable = schema.Tables.FirstOrDefault(t => referencedFullDbName == t.Name); if (referencedTable == null) { //try case-insensitive match //reason: MySql's Key_Column_Usage table contains both 'Northwind' and 'northwind' referencedTable = schema.Tables.FirstOrDefault(t => referencedFullDbName.ToLower() == t.Name.ToLower()); } if (referencedTable == null) { ReportForeignKeyError(schema, referencedFullDbName); } else { referencedTable.Type.Associations.Add(reverseAssociation); assoc.Type = referencedTable.Type.Name; } }
protected override void LoadConstraints(Database schema, SchemaName schemaName, IDbConnection conn, NameFormat nameFormat, Names names) { var constraints = ReadConstraints(conn, schemaName.DbName); foreach (DataConstraint constraint in constraints) { //find my table: string constraintFullDbName = GetFullDbName(constraint.TableName, constraint.TableSchema); DbLinq.Schema.Dbml.Table table = schema.Tables.FirstOrDefault(t => constraintFullDbName == t.Name); if (table == null) { WriteErrorLine("ERROR L100: Table '" + constraint.TableName + "' not found for column " + constraint.ColumnNameList); continue; } //if (table.Name.StartsWith("E")) // Logger.Write("---Dbg"); if (constraint.ConstraintType == "P") { //A) add primary key DbLinq.Schema.Dbml.Column pkColumn = table.Type.Columns.Where(c => constraint.ColumnNames.Contains(c.Name)).First(); pkColumn.IsPrimaryKey = true; } else if (constraint.ConstraintType == "R") { //if not PRIMARY, it's a foreign key. (constraint_type=="R") //both parent and child table get an [Association] DataConstraint referencedConstraint = constraints.FirstOrDefault(c => c.ConstraintName == constraint.ReverseConstraintName); if (constraint.ReverseConstraintName == null || referencedConstraint == null) { WriteErrorLine("ERROR L127: given R_contraint_name='" + constraint.ReverseConstraintName + "', unable to find parent constraint"); continue; } LoadForeignKey(schema, table, constraint.ColumnNameList, constraint.TableName, constraint.TableSchema, referencedConstraint.ColumnNameList, referencedConstraint.TableName, referencedConstraint.TableSchema, constraint.ConstraintName, nameFormat, names); } // custom type, this is a trigger else if (constraint.ConstraintType == "T" && constraint.ColumnNames.Count == 1) { var column = table.Type.Columns.Where(c => c.Name == constraint.ColumnNames[0]).First(); column.Expression = constraint.Expression; column.IsDbGenerated = true; } } //GuessSequencePopulatedFields(schema); }
protected override void LoadConstraints(Database schema, SchemaName schemaName, IDbConnection conn, NameFormat nameFormat, Names names) { //TableSorter.Sort(tables, constraints); //sort tables - parents first var foreignKeys = ReadConstraints(conn, schemaName.DbName); foreach (DataConstraint keyColRow in foreignKeys) { //find my table: string constraintFullDbName = GetFullDbName(keyColRow.TableName, keyColRow.TableSchema); DbLinq.Schema.Dbml.Table table = schema.Tables.FirstOrDefault(t => constraintFullDbName == t.Name); if (table == null) { WriteErrorLine("ERROR L138: Table '" + keyColRow.TableName + "' not found for column " + keyColRow.ColumnName); continue; } if (keyColRow.ConstraintType.Equals("P")) //'PRIMARY KEY' { //foreach (string pk_name in keyColRow.column_name_primaries) //{ DbLinq.Schema.Dbml.Column primaryKeyCol = table.Type.Columns.First(c => c.Name == keyColRow.ColumnName); primaryKeyCol.IsPrimaryKey = true; //} continue; } if (keyColRow.ConstraintType.Equals("R")) //'FOREIGN KEY' { // This is very bad... if (!names.ColumnsNames[keyColRow.ReferencedTableName].ContainsKey(keyColRow.ReferencedColumnName)) continue; LoadForeignKey(schema, table, keyColRow.ColumnName, keyColRow.TableName, keyColRow.TableSchema, keyColRow.ReferencedColumnName, keyColRow.ReferencedTableName, keyColRow.ReferencedTableSchema, keyColRow.ConstraintName, nameFormat, names); } } }
protected override void LoadStoredProcedures(Database schema, SchemaName schemaName, IDbConnection conn, NameFormat nameFormat) { var parameters = ReadParameters(conn, schemaName.DbName); foreach (var parameter in parameters) { var procedureName = CreateProcedureName(parameter.ProcedureName, parameter.Schema, nameFormat); Function function = schema.Functions.SingleOrDefault(f => f.Method == procedureName.MethodName); if (function == null) { function = new Function { Name = procedureName.DbName, Method = procedureName.MethodName }; schema.Functions.Add(function); } if (parameter.Name == null) { var returnParameter = new Return(); returnParameter.DbType = parameter.Type.SqlType; returnParameter.Type = MapDbType(parameter.Name, parameter.Type).ToString(); function.IsComposable = true; function.Return = returnParameter; } else { var functionParameter = new Parameter(); functionParameter.DbType = parameter.Type.SqlType; functionParameter.Type = MapDbType(parameter.Name, parameter.Type).ToString(); if (parameter.In) { if (parameter.Out) functionParameter.Direction = DbLinq.Schema.Dbml.ParameterDirection.InOut; else functionParameter.Direction = DbLinq.Schema.Dbml.ParameterDirection.In; } else functionParameter.Direction = DbLinq.Schema.Dbml.ParameterDirection.Out; var parameterName = CreateParameterName(parameter.Name, nameFormat); functionParameter.Name = parameterName.CallName; function.Parameters.Add(functionParameter); } } }
protected virtual bool WriteDataContextCtor(CodeWriter writer, Database schema, Type contextBaseType, ParameterDefinition[] parameters, string[] baseCallParameterNames, Type[] baseCallParameterTypes, GenerationContext context) { // if we have a contextBaseType, then check that we can do it if (contextBaseType != null) { var ctor = contextBaseType.GetConstructor(baseCallParameterTypes); if (ctor == null) return false; } using (writer.WriteCtor(SpecificationDefinition.Public, schema.Class, parameters, baseCallParameterNames)) { writer.WriteLine(writer.GetStatement(writer.GetMethodCallExpression("OnCreated"))); } writer.WriteLine(); return true; }
/// <summary> /// Checks for name conflicts, given lambdas to correct on conflicts /// </summary> /// <param name="schema"></param> /// <param name="tableNamedAssociationRenamer"></param> /// <param name="columnNamedAssociationRenamer"></param> protected virtual void CheckConstraintsName(Database schema, Func<Association, string> tableNamedAssociationRenamer, Func<Association, string> columnNamedAssociationRenamer) { foreach (var table in schema.Tables) { foreach (var association in table.Type.Associations) { var localAssociation = association; if (association.Member == table.Type.Name) association.Member = tableNamedAssociationRenamer(association); else if ((from column in table.Type.Columns where column.Member == localAssociation.Member select column).FirstOrDefault() != null) { association.Member = columnNamedAssociationRenamer(association); } } } }
public virtual Database Load(string databaseName, INameAliases nameAliases, NameFormat nameFormat, bool loadStoredProcedures, string contextNamespace, string entityNamespace) { // check if connection is open. Note: we may use something more flexible if (Connection.State != ConnectionState.Open) Connection.Open(); // get the database name. If we don't have one, take it from connection string... if (string.IsNullOrEmpty(databaseName)) databaseName = Connection.Database; // ... and if connection string doesn't provide a name, then throw an error if (string.IsNullOrEmpty(databaseName)) throw new ArgumentException("A database name is required. Please specify /database=<databaseName>"); var schemaName = NameFormatter.GetSchemaName(databaseName, GetExtraction(databaseName), nameFormat); var names = new Names(); var schema = new Database { Name = schemaName.DbName, Class = schemaName.ClassName, BaseType = typeof(DataContext).FullName, ContextNamespace = contextNamespace, EntityNamespace = entityNamespace, }; // order is important, we must have: // 1. tables // 2. columns // 3. constraints LoadTables(schema, schemaName, Connection, nameAliases, nameFormat, names); LoadColumns(schema, schemaName, Connection, nameAliases, nameFormat, names); LoadConstraints(schema, schemaName, Connection, nameFormat, names); if (loadStoredProcedures) LoadStoredProcedures(schema, schemaName, Connection, nameFormat); CheckNamesCaseSafety(schema); // check for duplicate names between properties CheckNames(schema); // generate backing fields name (since we have here correct names) GenerateStorageFields(schema); return schema; }
protected override void LoadStoredProcedures(Database schema, SchemaName schemaName, IDbConnection conn, NameFormat nameFormat) { var procs = ReadProcedures(conn, schemaName.DbName); foreach (DataStoredProcedure proc in procs) { var procedureName = CreateProcedureName(proc.specific_name, proc.db, nameFormat); var func = new Function(); func.Name = procedureName.DbName; func.Method = procedureName.MethodName; func.IsComposable = string.Compare(proc.type, "FUNCTION") == 0; func.BodyContainsSelectStatement = proc.body != null && proc.body.IndexOf("select", StringComparison.OrdinalIgnoreCase) > -1; ParseProcParams(proc, func); schema.Functions.Add(func); } }
protected virtual void CheckNamesCaseSafety(Database schema) { schema.Name = Vendor.SqlProvider.GetSafeName(schema.Name); foreach (var table in schema.Table) { table.Name = Vendor.SqlProvider.GetSafeName(table.Name); foreach (var column in table.Type.Columns) { column.Name = Vendor.SqlProvider.GetSafeName(column.Name); } foreach (var association in table.Type.Associations) { association.Name = Vendor.SqlProvider.GetSafeName(association.Name); } } foreach (var storedProcedure in schema.Functions) { storedProcedure.Name = Vendor.SqlProvider.GetSafeName(storedProcedure.Name); } }
protected virtual CodeNamespace GenerateCodeDomModel(Database database) { CodeNamespace _namespace = new CodeNamespace(database.ContextNamespace); _namespace.Imports.Add(new CodeNamespaceImport("System")); _namespace.Imports.Add(new CodeNamespaceImport("System.ComponentModel")); _namespace.Imports.Add(new CodeNamespaceImport("System.Data")); _namespace.Imports.Add(new CodeNamespaceImport("System.Data.Linq.Mapping")); _namespace.Imports.Add(new CodeNamespaceImport("System.Diagnostics")); _namespace.Imports.Add(new CodeNamespaceImport("DbLinq.Linq")); _namespace.Imports.Add(new CodeNamespaceImport("DbLinq.Linq.Mapping")); _namespace.Comments.Add(new CodeCommentStatement(GenerateCommentBanner(database))); _namespace.Types.Add(GenerateContextClass(database)); foreach (Table table in database.Tables) _namespace.Types.Add(GenerateTableClass(table)); return _namespace; }
protected override void LoadStoredProcedures(Database schema, SchemaName schemaName, IDbConnection conn, NameFormat nameFormat) { var procs = ReadProcedures(conn, schemaName.DbName); //4a. determine unknown types Dictionary<long, string> typeOidToName = new Dictionary<long, string>(); foreach (DataStoredProcedure proc in procs) { if (proc.proallargtypes == null && !string.IsNullOrEmpty(proc.proargtypes)) proc.proallargtypes = "{" + proc.proargtypes.Replace(' ', ',') + "}"; //work around pgsql weirdness? } foreach (DataStoredProcedure proc in procs) { typeOidToName[proc.prorettype] = proc.formatted_prorettype; if (proc.proallargtypes == null) continue; //no args, no Oids to resolve, skip string[] argTypes1 = parseCsvString(proc.proallargtypes); //eg. {23,24,1043} var argTypes2 = from t in argTypes1 select long.Parse(t); foreach (long argType in argTypes2) { if (!typeOidToName.ContainsKey(argType)) typeOidToName[argType] = null; } } //4b. get names for unknown types GetTypeNames(conn, schemaName.DbName, typeOidToName); //4c. generate dbml objects foreach (DataStoredProcedure proc in procs) { DbLinq.Schema.Dbml.Function dbml_fct = ParseFunction(proc, typeOidToName, nameFormat); if (!SkipProc(dbml_fct.Name)) schema.Functions.Add(dbml_fct); } }
protected override void LoadStoredProcedures(Database schema, SchemaName schemaName, IDbConnection conn, NameFormat nameFormat) { // TODO: debug stored procedures support return; var procs = ReadProcedures(conn, schemaName.DbName); foreach (DataStoredProcedure proc in procs) { var procedureName = CreateProcedureName(proc.Name, proc.TableSchema, nameFormat); var func = new Function(); func.Name = procedureName.DbName; func.Method = procedureName.MethodName; func.IsComposable = string.Compare(proc.Type, "FUNCTION") == 0; func.BodyContainsSelectStatement = proc.BodyContainsSelectStatement; ParseProcParams(proc, func); schema.Functions.Add(func); } }
/// <summary> /// Checks for problematic names on columns /// We currently have 1 case, where column is equal to table name /// </summary> /// <param name="schema"></param> protected virtual void CheckColumnsName(Database schema) { foreach (var table in schema.Tables) { foreach (var column in table.Type.Columns) { // THE case if (column.Member == table.Type.Name) { // now, we try to append 1, then 2, etc. var appendValue = 0; for (; ; ) { var newColumnMember = column.Member + ++appendValue; if (!table.Type.Columns.Any(c => c.Member == newColumnMember)) { column.Member = newColumnMember; } } } } } }
/// <summary> /// guess which fields are populated by sequences. /// Mark them with [AutoGenId]. /// </summary> public static void GuessSequencePopulatedFields(DbLinq.Schema.Dbml.Database schema) { if (schema == null) { return; } foreach (DbLinq.Schema.Dbml.Table tbl in schema.Tables) { var q = from col in tbl.Type.Columns where col.IsPrimaryKey select col; List <DbLinq.Schema.Dbml.Column> cols = q.ToList(); bool canBeFromSequence = cols.Count == 1 && (!cols[0].CanBeNull) && (cols[0].DbType == "NUMBER" || cols[0].DbType == "INTEGER"); if (canBeFromSequence) { //TODO: query sequences, store sequence name. //in the meantime, assume naming convention similar to 'Products_seq' cols[0].IsDbGenerated = true; } } }
public void GenerateCode(Parameters parameters, Database dbSchema, ISchemaLoader schemaLoader, string filename) { ICodeGenerator codeGenerator = FindCodeGenerator(parameters, filename); if (codeGenerator == null) throw new ArgumentException("Please specify either a /language or a /code file"); if (string.IsNullOrEmpty(filename)) filename = dbSchema.Class; if (String.IsNullOrEmpty(Path.GetExtension(filename))) filename += codeGenerator.Extension; using (var streamWriter = new StreamWriter(filename)) { var generationContext = new GenerationContext(parameters, schemaLoader); codeGenerator.Write(streamWriter, dbSchema, generationContext); } }
protected void RemoveSchemaFromTables(Database schema) { foreach (var table in schema.Table) { string[] nameAndSchema = table.Name.Split('.'); table.Name = nameAndSchema[nameAndSchema.Length - 1]; } }
protected void WriteSchema(Database dbSchema, ISchemaLoader schemaLoader, Parameters parameters) { if (parameters.Dbml != null) { //we are supposed to write out a DBML file and exit parameters.Write("<<< Writing file '{0}'", parameters.Dbml); using (Stream dbmlFile = File.Create(parameters.Dbml)) { DbmlSerializer.Write(dbmlFile, dbSchema); } } else { if (!parameters.Schema) RemoveSchemaFromTables(dbSchema); // extract filename from output filename, database schema or schema name string filename = parameters.Code; if (string.IsNullOrEmpty(filename) && !string.IsNullOrEmpty(parameters.Database)) filename = parameters.Database.Replace("\"", ""); if (string.IsNullOrEmpty(filename)) filename = dbSchema.Name; // TODO: move such check to runtime. schemaLoader.CheckNamesSafety(dbSchema); parameters.Write("<<< writing C# classes in file '{0}'", filename); GenerateCode(parameters, dbSchema, schemaLoader, filename); } }
/// <summary> /// Writes class ctor. /// EntitySet initializations /// </summary> /// <param name="writer"></param> /// <param name="table"></param> /// <param name="schema"></param> /// <param name="context"></param> protected virtual void WriteClassCtor(CodeWriter writer, Table table, Database schema, GenerationContext context) { using (writer.WriteRegion("ctor")) using (writer.WriteCtor(SpecificationDefinition.Public, table.Type.Name, new ParameterDefinition[0], null)) { // children are EntitySet foreach (var child in GetClassChildren(table)) { // if the association has a storage, we use it. Otherwise, we use the property name var entitySetMember = child.Storage ?? child.Member; writer.WriteLine(writer.GetStatement( writer.GetAssignmentExpression( entitySetMember, writer.GetNewExpression(writer.GetMethodCallExpression( writer.GetGenericName(TypeExtensions.GetShortName(typeof(EntitySet<>)), child.Type), GetChildAttachMethodName(child), GetChildDetachMethodName(child) )) ) )); } // the parents are the entities referenced by a FK. So a "parent" is an EntityRef foreach (var parent in GetClassParents(table)) { var entityRefMember = parent.Storage; writer.WriteLine(writer.GetStatement( writer.GetAssignmentExpression( entityRefMember, writer.GetNewExpression(writer.GetMethodCallExpression( writer.GetGenericName(TypeExtensions.GetShortName(typeof(EntityRef<>)), parent.Type) )) ) )); } writer.WriteLine(writer.GetStatement(writer.GetMethodCallExpression("OnCreated"))); } }
/// <summary> /// Writes attach/detach method /// </summary> /// <param name="writer"></param> /// <param name="table"></param> /// <param name="schema"></param> /// <param name="context"></param> protected virtual void WriteClassChildrenAttachment(CodeWriter writer, Table table, Database schema, GenerationContext context) { var children = GetClassChildren(table).ToList(); if (children.Count > 0) { using (writer.WriteRegion("Attachement handlers")) { foreach (var child in children) { // the reverse child is the association seen from the child // we're going to use it... var reverseChild = schema.GetReverseAssociation(child); // ... to get the parent name var memberName = reverseChild.Member; var entityParameter = new ParameterDefinition { Name = "entity", LiteralType = child.Type }; // the Attach event handler sets the child entity parent to "this" using (writer.WriteMethod(SpecificationDefinition.Private, GetChildAttachMethodName(child), null, entityParameter)) { writer.WriteLine( writer.GetStatement( writer.GetAssignmentExpression( writer.GetMemberExpression(entityParameter.Name, memberName), writer.GetThisExpression()))); } writer.WriteLine(); // the Detach event handler sets the child entity parent to null using (writer.WriteMethod(SpecificationDefinition.Private, GetChildDetachMethodName(child), null, entityParameter)) { writer.WriteLine( writer.GetStatement( writer.GetAssignmentExpression( writer.GetMemberExpression(entityParameter.Name, memberName), writer.GetNullExpression()))); } writer.WriteLine(); } } } }
protected virtual void WriteClassParent(CodeWriter writer, Association parent, bool hasDuplicates, Database schema, GenerationContext context) { // the following is apparently useless DbLinq.Schema.Dbml.Table targetTable = schema.Tables.FirstOrDefault(t => t.Type.Name == parent.Type); if (targetTable == null) { //Logger.Write(Level.Error, "ERROR L191 target table type not found: " + parent.Type + " (processing " + parent.Name + ")"); return; } string member = parent.Member; string storageField = parent.Storage; // TODO: remove this if (member == parent.ThisKey) { member = parent.ThisKey + targetTable.Type.Name; //repeat name to prevent collision (same as Linq) storageField = "_x_" + parent.Member; } writer.WriteField(SpecificationDefinition.Private, storageField, writer.GetGenericName(TypeExtensions.GetShortName(typeof(EntityRef<>)), targetTable.Type.Name)); var storageAttribute = NewAttributeDefinition<AssociationAttribute>(); storageAttribute["Storage"] = storageField; storageAttribute["OtherKey"] = parent.OtherKey; storageAttribute["ThisKey"] = parent.ThisKey; storageAttribute["Name"] = parent.Name; storageAttribute["IsForeignKey"] = parent.IsForeignKey; SpecificationDefinition specifications; if (parent.AccessModifierSpecified) specifications = GetSpecificationDefinition(parent.AccessModifier); else specifications = SpecificationDefinition.Public; if (parent.ModifierSpecified) specifications |= GetSpecificationDefinition(parent.Modifier); var propertyName = hasDuplicates ? member + "_" + string.Join("", parent.TheseKeys.ToArray()) : member; using (writer.WriteAttribute(storageAttribute)) using (writer.WriteAttribute(NewAttributeDefinition<DebuggerNonUserCodeAttribute>())) using (writer.WriteProperty(specifications, propertyName, targetTable.Type.Name)) { string storage = writer.GetMemberExpression(storageField, "Entity"); using (writer.WritePropertyGet()) { writer.WriteLine(writer.GetReturnStatement(storage)); } using (writer.WritePropertySet()) { // algorithm is: // 1.1. must be different than previous value // 1.2. or HasLoadedOrAssignedValue is false (but why?) // 2. implementations before change // 3. if previous value not null // 3.1. place parent in temp variable // 3.2. set [Storage].Entity to null // 3.3. remove it from parent list // 4. assign value to [Storage].Entity // 5. if value is not null // 5.1. add it to parent list // 5.2. set FK members with entity keys // 6. else // 6.1. set FK members to defaults (null or 0) // 7. implementationas after change //writer.WriteLine(writer.GetStatement(writer.GetAssignmentExpression(storage, writer.GetPropertySetValueExpression()))); var entityMember = writer.GetMemberExpression(parent.Storage, "Entity"); // 1.1 using (writer.WriteIf(writer.GetDifferentExpression(writer.GetPropertySetValueExpression(), entityMember))) { var otherAssociation = schema.GetReverseAssociation(parent); // 2. code before the change // TODO change interface to require a member instead of a column //foreach (IImplementation implementation in context.Implementations()) // implementation.WritePropertyBeforeSet(writer, ???, context); // 3. using (writer.WriteIf(writer.GetDifferentExpression(entityMember, writer.GetNullExpression()))) { var previousEntityRefName = "previous" + parent.Type; // 3.1. writer.WriteLine(writer.GetStatement( writer.GetVariableDeclarationInitialization(parent.Type, previousEntityRefName, entityMember) )); // 3.2. writer.WriteLine(writer.GetStatement( writer.GetAssignmentExpression(entityMember, writer.GetNullExpression()) )); // 3.3. writer.WriteLine(writer.GetStatement( writer.GetMethodCallExpression( writer.GetMemberExpression(writer.GetMemberExpression(previousEntityRefName, otherAssociation.Member), "Remove"), writer.GetThisExpression()) )); } // 4. writer.WriteLine(writer.GetStatement( writer.GetAssignmentExpression(entityMember, writer.GetPropertySetValueExpression()) )); // 5. if value is null or not writer.WriteRawIf(writer.GetDifferentExpression(writer.GetPropertySetValueExpression(), writer.GetNullExpression())); // 5.1. writer.WriteLine(writer.GetStatement( writer.GetMethodCallExpression( writer.GetMemberExpression(writer.GetMemberExpression(writer.GetPropertySetValueExpression(), otherAssociation.Member), "Add"), writer.GetThisExpression()) )); // 5.2 var table = schema.Tables.Single(t => t.Type.Associations.Contains(parent)); var childKeys = parent.TheseKeys.ToArray(); var childColumns = (from ck in childKeys select table.Type.Columns.Single(c => c.Member == ck)) .ToArray(); var parentKeys = parent.OtherKeys.ToArray(); for (int keyIndex = 0; keyIndex < parentKeys.Length; keyIndex++) { writer.WriteLine(writer.GetStatement(writer.GetAssignmentExpression( childColumns[keyIndex].Storage ?? childColumns[keyIndex].Member, writer.GetMemberExpression(writer.GetPropertySetValueExpression(), parentKeys[keyIndex]) ))); } // 6. writer.WriteRawElse(); // 6.1. for (int keyIndex = 0; keyIndex < parentKeys.Length; keyIndex++) { var column = table.Type.Columns.Single(c => c.Member == childKeys[keyIndex]); var columnType = System.Type.GetType(column.Type); var columnLiteralType = columnType != null ? writer.GetLiteralType(columnType) : column.Type; writer.WriteLine(writer.GetStatement(writer.GetAssignmentExpression( childColumns[keyIndex].Storage ?? childColumns[keyIndex].Member, column.CanBeNull ? writer.GetNullExpression() : writer.GetNullValueExpression(columnLiteralType) ))); } writer.WriteRawEndif(); // 7. code after change // TODO change interface to require a member instead of a column //foreach (IImplementation implementation in context.Implementations()) // implementation.WritePropertyAfterSet(writer, ???, context); } } } writer.WriteLine(); }
protected virtual void WriteClassParents(CodeWriter writer, Table table, Database schema, GenerationContext context) { var parents = GetClassParents(table).ToList(); if (parents.Count > 0) { using (writer.WriteRegion("Parents")) { foreach (var parent in parents) { bool hasDuplicates = (from p in parents where p.Member == parent.Member select p).Count() > 1; WriteClassParent(writer, parent, hasDuplicates, schema, context); } } } }
private void WriteClassChild(CodeWriter writer, Association child, bool hasDuplicates, Database schema, GenerationContext context) { // the following is apparently useless DbLinq.Schema.Dbml.Table targetTable = schema.Tables.FirstOrDefault(t => t.Type.Name == child.Type); if (targetTable == null) { //Logger.Write(Level.Error, "ERROR L143 target table class not found:" + child.Type); return; } var storageAttribute = NewAttributeDefinition<AssociationAttribute>(); storageAttribute["Storage"] = child.Storage; storageAttribute["OtherKey"] = child.OtherKey; storageAttribute["ThisKey"] = child.ThisKey; storageAttribute["Name"] = child.Name; SpecificationDefinition specifications; if (child.AccessModifierSpecified) specifications = GetSpecificationDefinition(child.AccessModifier); else specifications = SpecificationDefinition.Public; if (child.ModifierSpecified) specifications |= GetSpecificationDefinition(child.Modifier); var propertyName = hasDuplicates ? child.Member + "_" + string.Join("", child.OtherKeys.ToArray()) : child.Member; var propertyType = writer.GetGenericName(TypeExtensions.GetShortName(typeof(EntitySet<>)), child.Type); if (child.Storage != null) writer.WriteField(SpecificationDefinition.Private, child.Storage, propertyType); using (writer.WriteAttribute(storageAttribute)) using (writer.WriteAttribute(NewAttributeDefinition<DebuggerNonUserCodeAttribute>())) using (writer.WriteProperty(specifications, propertyName, writer.GetGenericName(TypeExtensions.GetShortName(typeof(EntitySet<>)), child.Type))) { // if we have a backing field, use it if (child.Storage != null) { // the getter returns the field using (writer.WritePropertyGet()) { writer.WriteLine(writer.GetReturnStatement( child.Storage )); } // the setter assigns the field using (writer.WritePropertySet()) { writer.WriteLine(writer.GetStatement( writer.GetAssignmentExpression( child.Storage, writer.GetPropertySetValueExpression()) )); } } // otherwise, use automatic property else writer.WriteAutomaticPropertyGetSet(); } writer.WriteLine(); }
protected virtual void WriteClassChildren(CodeWriter writer, Table table, Database schema, GenerationContext context) { var children = GetClassChildren(table).ToList(); if (children.Count > 0) { using (writer.WriteRegion("Children")) { foreach (var child in children) { bool hasDuplicates = (from c in children where c.Member == child.Member select c).Count() > 1; WriteClassChild(writer, child, hasDuplicates, schema, context); } } } }