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 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 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); } } }
/// <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, DbLinq.Schema.SchemaName schemaName, IDbConnection conn, DbLinq.Schema.NameFormat nameFormat, Names names) { DbConnection c = (DbConnection)conn; var foreignKeys = GetForeignKeys(c); int iConstName = foreignKeys.Columns.IndexOf("CONSTRAINT_NAME"); int iConstType = foreignKeys.Columns.IndexOf("CONSTRAINT_TYPE"); int iFromColumn = foreignKeys.Columns.IndexOf("FKEY_FROM_COLUMN"); int iFromSchema = foreignKeys.Columns.IndexOf("TABLE_SCHEMA"); int iFromTable = foreignKeys.Columns.IndexOf("TABLE_NAME"); int iToColumn = foreignKeys.Columns.IndexOf("FKEY_TO_COLUMN"); int iToSchema = foreignKeys.Columns.IndexOf("FKEY_TO_SCHEMA"); int iToTable = foreignKeys.Columns.IndexOf("FKEY_TO_TABLE"); if (iConstName < 0 || iConstType < 0 || iFromColumn < 0 || iFromSchema < 0 || iFromTable < 0 || iToColumn < 0 || iToSchema < 0 || iToTable < 0) { WriteErrorLine("Database connection '{0}' doesn't support querying foreign key information.", c.GetType().Name); return; } foreach (DataRow r in foreignKeys.Rows) { var fromTable = UnquoteSqlName(GetValue <string>(r, iFromTable, null)); var fromSchema = UnquoteSqlName(GetValue <string>(r, iFromSchema, null)); var fromColumn = UnquoteSqlName(GetValue <string>(r, iFromColumn, null)); string fullFromTable = GetFullDbName(fromTable, fromSchema); DbLinq.Schema.Dbml.Table table = schema.Tables.FirstOrDefault(t => fullFromTable == t.Name); if (table == null) { WriteErrorLine("ERROR L46: Table '" + fromTable + "' not found for column " + fromColumn); continue; } var constraintType = GetValue <string>(r, iConstType, null); var toTable = UnquoteSqlName(GetValue <string>(r, iToTable, null)); if (constraintType == "FOREIGN KEY" && toTable != null) { var constraintName = GetValue(r, iConstName, (string)null); var toColumn = UnquoteSqlName(GetValue <string>(r, iToColumn, null)); var toSchema = UnquoteSqlName(GetValue <string>(r, iToSchema, null)); LoadForeignKey(schema, table, fromColumn, fromTable, fromSchema, toColumn, toTable, toSchema, constraintName, nameFormat, names); } } }
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.ColumnName); 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 => c.Name == constraint.ColumnName).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.ColumnName, constraint.TableName, constraint.TableSchema, referencedConstraint.ColumnName, referencedConstraint.TableName, referencedConstraint.TableSchema, constraint.ConstraintName, nameFormat, names); } // custom type, this is a trigger else if (constraint.ConstraintType == "T" && constraint.ColumnName != null) { var column = table.Type.Columns.Where(c => c.Name == constraint.ColumnName).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 LoadConstraints(Database schema, SchemaName schemaName, IDbConnection conn, NameFormat nameFormat, Names names) { foreach (var table in schema.Table) { table.Name = table.Name.Split('.').Last(); } var constraints = ReadConstraints(conn, schemaName.DbName); //sort tables - parents first (this is moving to SchemaPostprocess) //TableSorter.Sort(tables, constraints); // Deal with non existing foreign key database if (constraints != null) { foreach (DataConstraint keyColRow in constraints) { //find my table: string tableFullDbName = keyColRow.TableName;// GetFullDbName(keyColRow.TableName, keyColRow.TableSchema); DbLinq.Schema.Dbml.Table table = schema.Tables.FirstOrDefault(t => tableFullDbName == t.Name); 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); } else { //A) add primary key DbLinq.Schema.Dbml.Column primaryKeyCol = table.Type.Columns.First(c => c.Name == keyColRow.ColumnName); primaryKeyCol.IsPrimaryKey = true; } } } }
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); } } } }
/// <summary> /// Returns all parents (ie member referenced as EntityRef) /// </summary> /// <param name="table"></param> /// <returns></returns> protected virtual IEnumerable<Association> GetClassParents(Table table) { return table.Type.Associations.Where(a => a.IsForeignKey); }
void WriteCustomTypes(CodeTypeDeclaration entity, Table table) { // detect required custom types foreach (var column in table.Type.Columns) { var extendedType = column.ExtendedType; var enumType = extendedType as EnumType; if (enumType != null) { Context.ExtendedTypes[column] = new GenerationContext.ExtendedTypeAndName { Type = column.ExtendedType, Table = table }; } } var customTypesNames = new List<string>(); // create names and avoid conflits foreach (var extendedTypePair in Context.ExtendedTypes) { if (extendedTypePair.Value.Table != table) continue; if (string.IsNullOrEmpty(extendedTypePair.Value.Type.Name)) { string name = extendedTypePair.Key.Member + "Type"; for (; ; ) { if ((from t in Context.ExtendedTypes.Values where t.Type.Name == name select t).FirstOrDefault() == null) { extendedTypePair.Value.Type.Name = name; break; } // at 3rd loop, it will look ugly, however we will never go there name = extendedTypePair.Value.Table.Type.Name + name; } } customTypesNames.Add(extendedTypePair.Value.Type.Name); } // write custom types if (customTypesNames.Count > 0) { var customTypes = new List<CodeTypeDeclaration>(customTypesNames.Count); foreach (var extendedTypePair in Context.ExtendedTypes) { if (extendedTypePair.Value.Table != table) continue; var extendedType = extendedTypePair.Value.Type; var enumValue = extendedType as EnumType; if (enumValue != null) { var enumType = new CodeTypeDeclaration(enumValue.Name) { TypeAttributes = TypeAttributes.Public, IsEnum = true, }; customTypes.Add(enumType); var orderedValues = from nv in enumValue orderby nv.Value select nv; int currentValue = 1; foreach (var nameValue in orderedValues) { var field = new CodeMemberField() { Name = nameValue.Key, }; enumType.Members.Add(field); if (nameValue.Value != currentValue) { currentValue = nameValue.Value; field.InitExpression = new CodePrimitiveExpression(nameValue.Value); } currentValue++; } } } if (customTypes.Count == 0) return; customTypes.First().StartDirectives.Add(new CodeRegionDirective(CodeRegionMode.Start, string.Format("Custom type definitions for {0}", string.Join(", ", customTypesNames.ToArray())))); customTypes.Last().EndDirectives.Add(new CodeRegionDirective(CodeRegionMode.End, null)); entity.Members.AddRange(customTypes.ToArray()); } }
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 WriteClassEqualsAndHash(CodeWriter writer, Table table, GenerationContext context) { List<DbLinq.Schema.Dbml.Column> primaryKeys = table.Type.Columns.Where(c => c.IsPrimaryKey).ToList(); if (primaryKeys.Count == 0) { writer.WriteLine("#warning L189 table {0} has no primary key. Multiple C# objects will refer to the same row.", table.Name); return; } using (writer.WriteRegion(string.Format("GetHashCode(), Equals() - uses column {0} to look up objects in liveObjectMap", string.Join(", ", primaryKeys.Select(pk => pk.Member).ToList().ToArray())))) { // GetHashCode using (writer.WriteMethod(SpecificationDefinition.Public | SpecificationDefinition.Override, "GetHashCode", typeof(int))) { string hashCode = null; foreach (var primaryKey in primaryKeys) { var member = writer.GetVariableExpression(primaryKey.Storage); string primaryKeyHashCode = writer.GetMethodCallExpression(writer.GetMemberExpression(member, "GetHashCode")); if (primaryKey.CanBeNull || GetType(primaryKey.Type, false).IsClass) // this patch to ensure that even if DB does not allow nulls, // our in-memory object won't generate a fault { var isNullExpression = writer.GetEqualExpression(member, writer.GetNullExpression()); var nullExpression = writer.GetLiteralValue(0); primaryKeyHashCode = "(" + writer.GetTernaryExpression(isNullExpression, nullExpression, primaryKeyHashCode) + ")"; } if (string.IsNullOrEmpty(hashCode)) hashCode = primaryKeyHashCode; else hashCode = writer.GetXOrExpression(hashCode, primaryKeyHashCode); } writer.WriteLine(writer.GetReturnStatement(hashCode)); } writer.WriteLine(); // Equals string otherAsObject = "o"; using (writer.WriteMethod(SpecificationDefinition.Public | SpecificationDefinition.Override, "Equals", typeof(bool), new ParameterDefinition { Type = typeof(object), Name = otherAsObject })) { string other = "other"; writer.WriteLine(writer.GetStatement(writer.GetAssignmentExpression( writer.GetDeclarationExpression(other, table.Type.Name), writer.GetCastExpression(otherAsObject, table.Type.Name, false)))); using (writer.WriteIf(writer.GetEqualExpression(other, writer.GetNullExpression()))) { writer.WriteLine(writer.GetReturnStatement(writer.GetLiteralValue(false))); } string andExpression = null; foreach (var primaryKey in primaryKeys) { var member = writer.GetVariableExpression(primaryKey.Storage); string primaryKeyTest = writer.GetMethodCallExpression( writer.GetMemberExpression( writer.GetMemberExpression( writer.GetGenericName("System.Collections.Generic.EqualityComparer", primaryKey.Type), "Default"), "Equals"), member, writer.GetMemberExpression(other, member)); if (string.IsNullOrEmpty(andExpression)) andExpression = primaryKeyTest; else andExpression = writer.GetAndExpression(andExpression, primaryKeyTest); } writer.WriteLine(writer.GetReturnStatement(andExpression)); } } }
/// <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(); } } } }
bool ValidateAssociations(Database database, Table table) { bool error = false; foreach (var association in table.Type.Associations) { var otherType = database.Tables.Single(t => t.Type.Name == association.Type).Type; var otherAssociation = otherType.Associations.Single(a => a.Type == table.Type.Name && a.ThisKey == association.OtherKey); var otherColumn = otherType.Columns.Single(c => c.Member == association.OtherKey); if (association.CardinalitySpecified && association.Cardinality == Cardinality.Many && association.IsForeignKey) { error = true; Log.WriteErrorLine("Error DBML1059: The IsForeignKey attribute of the Association element '{0}' of the Type element '{1}' cannnot be '{2}' when the Cardinality attribute is '{3}'.", association.Name, table.Type.Name, association.IsForeignKey, association.Cardinality); } } return error; }
void GenerateEntityChildrenAttachment(CodeTypeDeclaration entity, Table table, Database schema) { var children = GetClassChildren(table).ToList(); if (!children.Any()) return; var havePrimaryKeys = table.Type.Columns.Any(c => c.IsPrimaryKey); var handlers = new List<CodeTypeMember>(); 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 sendPropertyChanging = new CodeExpressionStatement( new CodeMethodInvokeExpression( new CodeMethodReferenceExpression(thisReference, "SendPropertyChanging"))); var attach = new CodeMemberMethod() { Name = child.Member + "_Attach", Parameters = { new CodeParameterDeclarationExpression(child.Type, "entity"), }, }; handlers.Add(attach); if (havePrimaryKeys) attach.Statements.Add(sendPropertyChanging); attach.Statements.Add( new CodeAssignStatement( new CodePropertyReferenceExpression(new CodeVariableReferenceExpression("entity"), memberName), thisReference)); var detach = new CodeMemberMethod() { Name = child.Member + "_Detach", Parameters = { new CodeParameterDeclarationExpression(child.Type, "entity"), }, }; handlers.Add(detach); if (havePrimaryKeys) detach.Statements.Add(sendPropertyChanging); detach.Statements.Add( new CodeAssignStatement( new CodePropertyReferenceExpression(new CodeVariableReferenceExpression("entity"), memberName), new CodePrimitiveExpression(null))); } if (handlers.Count == 0) return; handlers.First().StartDirectives.Add(new CodeRegionDirective(CodeRegionMode.Start, "Attachment handlers")); handlers.Last().EndDirectives.Add(new CodeRegionDirective(CodeRegionMode.End, null)); entity.Members.AddRange(handlers.ToArray()); }
/// <summary> /// Gets the primary keys. /// </summary> /// <param name="table">The table.</param> /// <returns></returns> protected static List<string> GetPrimaryKeys(Table table) { return (from c in table.Type.Columns where c.IsPrimaryKey select c.Name).ToList(); }
void GenerateEntityParents(CodeTypeDeclaration entity, Table table, Database schema) { var parents = table.Type.Associations.Where(a => a.IsForeignKey); if (!parents.Any()) return; var parentMembers = new List<CodeTypeMember>(); 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); // 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 + ")"); continue; } string member = parent.Member; string storageField = GetStorageFieldName(parent); // 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; } var parentType = new CodeTypeReference(targetTable.Type.Name); entity.Members.Add(new CodeMemberField(new CodeTypeReference("EntityRef", parentType), storageField) { InitExpression = new CodeObjectCreateExpression(new CodeTypeReference("EntityRef", parentType)), }); var parentName = hasDuplicates ? member + "_" + string.Join("", parent.TheseKeys.ToArray()) : member; var property = new CodeMemberProperty() { Name = parentName, Type = parentType, Attributes = ToMemberAttributes(parent), CustomAttributes = { new CodeAttributeDeclaration("Association", new CodeAttributeArgument("Storage", new CodePrimitiveExpression(storageField)), new CodeAttributeArgument("OtherKey", new CodePrimitiveExpression(parent.OtherKey)), new CodeAttributeArgument("ThisKey", new CodePrimitiveExpression(parent.ThisKey)), new CodeAttributeArgument("Name", new CodePrimitiveExpression(parent.Name)), new CodeAttributeArgument("IsForeignKey", new CodePrimitiveExpression(parent.IsForeignKey))), new CodeAttributeDeclaration("DebuggerNonUserCode"), }, }; parentMembers.Add(property); property.GetStatements.Add(new CodeMethodReturnStatement( new CodePropertyReferenceExpression( new CodeFieldReferenceExpression(thisReference, storageField), "Entity"))); // 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 var otherAssociation = schema.GetReverseAssociation(parent); var parentEntity = new CodePropertyReferenceExpression( new CodeFieldReferenceExpression(thisReference, storageField), "Entity"); var parentTable = 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(); property.SetStatements.Add(new CodeConditionStatement( // 1.1 ValuesAreNotEqual_Ref(parentEntity, new CodePropertySetValueReferenceExpression()), // 2. TODO: code before the change // 3. new CodeConditionStatement( ValueIsNotNull(parentEntity), // 3.1 new CodeVariableDeclarationStatement(parentType, "previous" + parent.Type, parentEntity), // 3.2 new CodeAssignStatement(parentEntity, new CodePrimitiveExpression(null)), // 3.3 new CodeExpressionStatement( new CodeMethodInvokeExpression( new CodeMethodReferenceExpression( new CodePropertyReferenceExpression( new CodeVariableReferenceExpression("previous" + parent.Type), otherAssociation.Member), "Remove"), thisReference))), // 4. new CodeAssignStatement(parentEntity, new CodePropertySetValueReferenceExpression()), // 5. if value is null or not... new CodeConditionStatement( ValueIsNotNull(new CodePropertySetValueReferenceExpression()), // 5.1 new CodeStatement[]{ new CodeExpressionStatement( new CodeMethodInvokeExpression( new CodeMethodReferenceExpression( new CodePropertyReferenceExpression( new CodePropertySetValueReferenceExpression(), otherAssociation.Member), "Add"), thisReference)) // 5.2 }.Concat(Enumerable.Range(0, parentKeys.Length).Select(i => (CodeStatement) new CodeAssignStatement( new CodeVariableReferenceExpression(GetStorageFieldName(childColumns[i])), new CodePropertyReferenceExpression( new CodePropertySetValueReferenceExpression(), parentKeys[i])) )).ToArray(), // 6. Enumerable.Range(0, parentKeys.Length).Select(i => { var column = parentTable.Type.Columns.Single(c => c.Member == childKeys[i]); return (CodeStatement) new CodeAssignStatement( new CodeVariableReferenceExpression(GetStorageFieldName(childColumns[i])), column.CanBeNull ? (CodeExpression) new CodePrimitiveExpression(null) : (CodeExpression) new CodeDefaultValueExpression(new CodeTypeReference(column.Type))); }).ToArray()) // 7: TODO )); } if (parentMembers.Count == 0) return; parentMembers.First().StartDirectives.Add(new CodeRegionDirective(CodeRegionMode.Start, "Parents")); parentMembers.Last().EndDirectives.Add(new CodeRegionDirective(CodeRegionMode.End, null)); entity.Members.AddRange(parentMembers.ToArray()); }
/// <summary> /// Loads the columns. /// </summary> /// <param name="schema">The schema.</param> /// <param name="schemaName">Name of the schema.</param> /// <param name="conn">The conn.</param> /// <param name="nameAliases">The name aliases.</param> /// <param name="nameFormat">The name format.</param> /// <param name="names">The names.</param> protected void LoadColumns(Database schema, SchemaName schemaName, IDbConnection conn, INameAliases nameAliases, NameFormat nameFormat, Names names) { var columnRows = ReadColumns(conn, schemaName.DbName); foreach (var columnRow in columnRows) { var columnName = CreateColumnName(columnRow.ColumnName, columnRow.TableName, columnRow.TableSchema, nameAliases, nameFormat); names.AddColumn(columnRow.TableName, columnName); //find which table this column belongs to string fullColumnDbName = GetFullDbName(columnRow.TableName, columnRow.TableSchema); DbLinq.Schema.Dbml.Table tableSchema = schema.Tables.FirstOrDefault(tblSchema => fullColumnDbName == tblSchema.Name); if (tableSchema == null) { WriteErrorLine("ERROR L46: Table '" + columnRow.TableName + "' not found for column " + columnRow.ColumnName); continue; } var column = new Column(); column.Name = columnName.DbName; column.Member = columnName.PropertyName; column.DbType = columnRow.FullType; if (columnRow.PrimaryKey.HasValue) { column.IsPrimaryKey = columnRow.PrimaryKey.Value; } bool?generated = (nameAliases != null) ? nameAliases.GetColumnGenerated(columnRow.ColumnName, columnRow.TableName, columnRow.TableSchema) : null; if (!generated.HasValue) { generated = columnRow.Generated; } if (generated.HasValue) { column.IsDbGenerated = generated.Value; } AutoSync?autoSync = (nameAliases != null) ? nameAliases.GetColumnAutoSync(columnRow.ColumnName, columnRow.TableName, columnRow.TableSchema) : null; if (autoSync.HasValue) { column.AutoSync = autoSync.Value; } // the Expression can originate from two sources: // 1. DefaultValue // 2. Expression // we use any valid source (we can't have both) if (column.IsDbGenerated && columnRow.DefaultValue != null) { column.Expression = columnRow.DefaultValue; } column.CanBeNull = columnRow.Nullable; string columnTypeAlias = nameAliases != null?nameAliases.GetColumnForcedType(columnRow.ColumnName, columnRow.TableName, columnRow.TableSchema) : null; var columnType = MapDbType(columnName.DbName, columnRow); var columnEnumType = columnType as EnumType; if (columnEnumType != null) { var enumType = column.SetExtendedTypeAsEnumType(); enumType.Name = columnEnumType.Name; foreach (KeyValuePair <string, int> enumValue in columnEnumType.EnumValues) { enumType[enumValue.Key] = enumValue.Value; } } else if (columnTypeAlias != null) { column.Type = columnTypeAlias; } else { column.Type = columnType.ToString(); } tableSchema.Type.Columns.Add(column); } }
protected virtual CodeTypeDeclaration GenerateTableClass(Table table) { var _class = new CodeTypeDeclaration() { IsClass = true, IsPartial = true, Name = table.Member, TypeAttributes = TypeAttributes.Public }; _class.CustomAttributes.Add(new CodeAttributeDeclaration("Table", new CodeAttributeArgument("Name", new CodePrimitiveExpression(table.Name)))); // Implement Constructor var constructor = new CodeConstructor() { Attributes = MemberAttributes.Public }; constructor.Statements.Add(new CodeExpressionStatement(new CodeMethodInvokeExpression(thisReference, "OnCreated"))); _class.Members.Add(constructor); // todo: implement INotifyPropertyChanging // Implement INotifyPropertyChanged _class.BaseTypes.Add(typeof(INotifyPropertyChanged)); var propertyChangedEvent = new CodeMemberEvent() { Type = new CodeTypeReference(typeof(PropertyChangedEventHandler)), Name = "PropertyChanged", Attributes = MemberAttributes.Public }; _class.Members.Add(propertyChangedEvent); var sendPropertyChangedMethod = new CodeMemberMethod() { Attributes = MemberAttributes.Family, Name = "SendPropertyChanged", Parameters = { new CodeParameterDeclarationExpression(typeof(System.String), "propertyName") } }; sendPropertyChangedMethod.Statements.Add ( new CodeConditionStatement ( new CodeSnippetExpression(propertyChangedEvent.Name + " != null"), // todo: covert this to CodeBinaryOperatorExpression new CodeExpressionStatement ( new CodeMethodInvokeExpression ( new CodeMethodReferenceExpression(thisReference, propertyChangedEvent.Name), thisReference, new CodeObjectCreateExpression(typeof(PropertyChangedEventArgs), new CodeArgumentReferenceExpression("propertyName")) ) ) ) ); _class.Members.Add(sendPropertyChangedMethod); // CodeDom does not currently support partial methods. This will be a problem for VB. Will probably be fixed in .net 4 _class.Members.Add(new CodeSnippetTypeMember("\tpartial void OnCreated();")); // todo: add these when the actually get called //partial void OnLoaded(); //partial void OnValidate(System.Data.Linq.ChangeAction action); // columns foreach (Column column in table.Type.Columns) { var type = new CodeTypeReference(column.Type); var columnMember = column.Member ?? column.Name; var field = new CodeMemberField(type, "_" + columnMember); _class.Members.Add(field); var fieldReference = new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), field.Name); // CodeDom does not currently support partial methods. This will be a problem for VB. Will probably be fixed in .net 4 string onChangingPartialMethodName = String.Format("On{0}Changing", columnMember); _class.Members.Add(new CodeSnippetTypeMember(String.Format("\tpartial void {0}({1} instance);", onChangingPartialMethodName, column.Type))); string onChangedPartialMethodName = String.Format("On{0}Changed", columnMember); _class.Members.Add(new CodeSnippetTypeMember(String.Format("\tpartial void {0}();", onChangedPartialMethodName))); var property = new CodeMemberProperty(); property.Type = type; property.Name = columnMember; property.Attributes = MemberAttributes.Public | MemberAttributes.Final; property.CustomAttributes.Add ( new CodeAttributeDeclaration ( "Column", new CodeAttributeArgument("Storage", new CodePrimitiveExpression(column.Storage)), new CodeAttributeArgument("Name", new CodePrimitiveExpression(column.Name)), new CodeAttributeArgument("DbType", new CodePrimitiveExpression(column.DbType)), new CodeAttributeArgument("CanBeNull", new CodePrimitiveExpression(column.CanBeNull)), new CodeAttributeArgument("IsPrimaryKey", new CodePrimitiveExpression(column.IsPrimaryKey)) ) ); property.CustomAttributes.Add(new CodeAttributeDeclaration("DebuggerNonUserCode")); property.GetStatements.Add(new CodeMethodReturnStatement(fieldReference)); property.SetStatements.Add ( new CodeConditionStatement ( new CodeSnippetExpression(field.Name + " != value"), // todo: covert this to CodeBinaryOperatorExpression new CodeExpressionStatement(new CodeMethodInvokeExpression(thisReference, onChangingPartialMethodName, new CodePropertySetValueReferenceExpression())), new CodeAssignStatement(fieldReference, new CodePropertySetValueReferenceExpression()), new CodeExpressionStatement(new CodeMethodInvokeExpression(thisReference, sendPropertyChangedMethod.Name, new CodePrimitiveExpression(property.Name))), new CodeExpressionStatement(new CodeMethodInvokeExpression(thisReference, onChangedPartialMethodName)) ) ); _class.Members.Add(property); } // TODO: implement associations // TODO: implement functions / procedures // TODO: Override Equals and GetHashCode return _class; }
void GenerateExtensibilityDeclarations(CodeTypeDeclaration entity, Table table) { var partialMethods = new[] { CreatePartialMethod("OnCreated") } .Concat(table.Type.Columns.Select(c => new[] { CreateChangedMethodDecl(c), CreateChangingMethodDecl(c) }) .SelectMany(md => md)).ToArray(); partialMethods.First().StartDirectives.Add(new CodeRegionDirective(CodeRegionMode.Start, "Extensibility Method Declarations")); partialMethods.Last().EndDirectives.Add(new CodeRegionDirective(CodeRegionMode.End, null)); entity.Members.AddRange(partialMethods); }
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); } } } }
IEnumerable<Association> GetClassChildren(Table table) { return table.Type.Associations.Where(a => !a.IsForeignKey); }
void GenerateEntityGetHashCodeAndEquals(CodeTypeDeclaration entity, Table table) { var primaryKeys = table.Type.Columns.Where(c => c.IsPrimaryKey); var pkCount = primaryKeys.Count(); if (pkCount == 0) { Warning("Table {0} has no primary key(s). Skipping /generate-equals-hash for this table.", table.Name); return; } entity.BaseTypes.Add(new CodeTypeReference(typeof(IEquatable<>)) { TypeArguments = { new CodeTypeReference(entity.Name) }, }); var method = new CodeMemberMethod() { Attributes = MemberAttributes.Public | MemberAttributes.Override, Name = "GetHashCode", ReturnType = new CodeTypeReference(typeof(int)), }; entity.Members.Add(method); method.Statements.Add(new CodeVariableDeclarationStatement(typeof(int), "hc", new CodePrimitiveExpression(0))); var numShifts = 32 / pkCount; int pki = 0; foreach (var pk in primaryKeys) { var shift = 1 << (pki++ * numShifts); // lack of exclusive-or means we instead split the 32-bit hash code value // into pkCount "chunks", each chunk being numShifts in size. // Thus, if there are two primary keys, the first primary key gets the // lower 16 bits, while the second primray key gets the upper 16 bits. CodeStatement update = new CodeAssignStatement( new CodeVariableReferenceExpression("hc"), new CodeBinaryOperatorExpression( new CodeVariableReferenceExpression("hc"), CodeBinaryOperatorType.BitwiseOr, new CodeBinaryOperatorExpression( new CodeMethodInvokeExpression( new CodeMethodReferenceExpression( new CodeVariableReferenceExpression(GetStorageFieldName(pk)), "GetHashCode")), CodeBinaryOperatorType.Multiply, new CodePrimitiveExpression(shift)))); var pkType = System.Type.GetType(pk.Type); if (pk.CanBeNull || (pkType != null && (pkType.IsClass || pkType.IsNullable()))) { update = new CodeConditionStatement( ValueIsNotNull(new CodeVariableReferenceExpression(GetStorageFieldName(pk))), update); } method.Statements.Add(update); } method.Statements.Add(new CodeMethodReturnStatement(new CodeVariableReferenceExpression("hc"))); method = new CodeMemberMethod() { Attributes = MemberAttributes.Public | MemberAttributes.Override, Name = "Equals", ReturnType = new CodeTypeReference(typeof(bool)), Parameters = { new CodeParameterDeclarationExpression(new CodeTypeReference(typeof(object)), "value"), }, }; entity.Members.Add(method); method.Statements.Add( new CodeConditionStatement( ValueIsNull(new CodeVariableReferenceExpression("value")), new CodeMethodReturnStatement(new CodePrimitiveExpression(false)))); method.Statements.Add( new CodeConditionStatement( ValuesAreNotEqual_Ref( new CodeMethodInvokeExpression( new CodeMethodReferenceExpression( new CodeVariableReferenceExpression("value"), "GetType")), new CodeMethodInvokeExpression( new CodeMethodReferenceExpression(thisReference, "GetType"))), new CodeMethodReturnStatement(new CodePrimitiveExpression(false)))); method.Statements.Add( new CodeVariableDeclarationStatement( new CodeTypeReference(entity.Name), "other", new CodeCastExpression(new CodeTypeReference(entity.Name), new CodeVariableReferenceExpression("value")))); method.Statements.Add( new CodeMethodReturnStatement( new CodeMethodInvokeExpression( new CodeMethodReferenceExpression(thisReference, "Equals"), new CodeVariableReferenceExpression("other")))); method = new CodeMemberMethod() { Attributes = MemberAttributes.Public, Name = "Equals", ReturnType = new CodeTypeReference(typeof(bool)), Parameters = { new CodeParameterDeclarationExpression(new CodeTypeReference(entity.Name), "value"), }, ImplementationTypes = { new CodeTypeReference("IEquatable", new CodeTypeReference(entity.Name)), }, }; entity.Members.Add(method); method.Statements.Add( new CodeConditionStatement( ValueIsNull(new CodeVariableReferenceExpression("value")), new CodeMethodReturnStatement(new CodePrimitiveExpression(false)))); CodeExpression equals = null; foreach (var pk in primaryKeys) { var compare = new CodeMethodInvokeExpression( new CodeMethodReferenceExpression( new CodePropertyReferenceExpression( new CodeTypeReferenceExpression( new CodeTypeReference("System.Collections.Generic.EqualityComparer", new CodeTypeReference(pk.Type))), "Default"), "Equals"), new CodeFieldReferenceExpression(thisReference, GetStorageFieldName(pk)), new CodeFieldReferenceExpression(new CodeVariableReferenceExpression("value"), GetStorageFieldName(pk))); equals = equals == null ? (CodeExpression) compare : (CodeExpression) new CodeBinaryOperatorExpression( equals, CodeBinaryOperatorType.BooleanAnd, compare); } method.Statements.Add( new CodeMethodReturnStatement(equals)); }
/// <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> /// Class headers are written at top of class /// They consist in specific headers writen by interface implementors /// </summary> /// <param name="writer"></param> /// <param name="table"></param> /// <param name="context"></param> private void WriteClassHeader(CodeWriter writer, Table table, GenerationContext context) { foreach (IImplementation implementation in context.Implementations()) implementation.WriteClassHeader(writer, table, context); }
void GenerateEntityChildren(CodeTypeDeclaration entity, Table table, Database schema) { var children = GetClassChildren(table); if (children.Any()) { var childMembers = new List<CodeTypeMember>(); foreach (var child in children) { bool hasDuplicates = (from c in children where c.Member == child.Member select c).Count() > 1; // the following is apparently useless var 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); continue; } var childType = new CodeTypeReference("EntitySet", new CodeTypeReference(child.Type)); var storage = GetStorageFieldName(child); entity.Members.Add(new CodeMemberField(childType, storage)); var childName = hasDuplicates ? child.Member + "_" + string.Join("", child.OtherKeys.ToArray()) : child.Member; var property = new CodeMemberProperty() { Name = childName, Type = childType, Attributes = ToMemberAttributes(child), CustomAttributes = { new CodeAttributeDeclaration("Association", new CodeAttributeArgument("Storage", new CodePrimitiveExpression(GetStorageFieldName(child))), new CodeAttributeArgument("OtherKey", new CodePrimitiveExpression(child.OtherKey)), new CodeAttributeArgument("ThisKey", new CodePrimitiveExpression(child.ThisKey)), new CodeAttributeArgument("Name", new CodePrimitiveExpression(child.Name))), new CodeAttributeDeclaration("DebuggerNonUserCode"), }, }; childMembers.Add(property); property.GetStatements.Add(new CodeMethodReturnStatement( new CodeFieldReferenceExpression(thisReference, storage))); property.SetStatements.Add(new CodeAssignStatement( new CodeFieldReferenceExpression(thisReference, storage), new CodePropertySetValueReferenceExpression())); } if (childMembers.Count == 0) return; childMembers.First().StartDirectives.Add(new CodeRegionDirective(CodeRegionMode.Start, "Children")); childMembers.Last().EndDirectives.Add(new CodeRegionDirective(CodeRegionMode.End, null)); entity.Members.AddRange(childMembers.ToArray()); } }
protected virtual void WriteClassExtensibilityDeclarations(CodeWriter writer, Table table, GenerationContext context) { using (writer.WriteRegion("Extensibility Method Definitions")) { writer.WriteLine("partial void OnCreated();"); foreach (var c in table.Type.Columns) { writer.WriteLine("partial void On{0}Changed();", c.Member); writer.WriteLine("partial void On{0}Changing({1} value);", c.Member, GetTypeOrExtendedType(writer, c)); } } }
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(); }
/// <summary> /// Writes all properties, depending on the use (simple property or FK) /// </summary> /// <param name="writer"></param> /// <param name="table"></param> /// <param name="context"></param> protected virtual void WriteClassProperties(CodeWriter writer, Table table, GenerationContext context) { foreach (var property in table.Type.Columns) { var property1 = property; var relatedAssociations = from a in table.Type.Associations where a.IsForeignKey && a.TheseKeys.Contains(property1.Name) select a; WriteClassProperty(writer, property, relatedAssociations, context); } }
/// <summary> /// Loads the tables in the given schema. /// </summary> /// <param name="schema">The schema.</param> /// <param name="schemaName">Name of the schema.</param> /// <param name="conn">The conn.</param> /// <param name="nameAliases">The name aliases.</param> /// <param name="nameFormat">The name format.</param> /// <param name="names">The names.</param> protected virtual void LoadTables(Database schema, SchemaName schemaName, IDbConnection conn, INameAliases nameAliases, NameFormat nameFormat, Names names) { var tables = ReadTables(conn, schemaName.DbName); foreach (var row in tables) { var tableName = CreateTableName(row.Name, row.Schema, nameAliases, nameFormat); names.TablesNames[tableName.DbName] = tableName; var table = new Table(); table.Name = tableName.DbName; table.Member = tableName.MemberName; table.Type.Name = tableName.ClassName; schema.Tables.Add(table); } }
protected CodeTypeDeclaration GenerateTableClass(Table table, Database database) { var _class = new CodeTypeDeclaration() { IsClass = true, IsPartial = true, Name = table.Type.Name, TypeAttributes = TypeAttributes.Public, CustomAttributes = { new CodeAttributeDeclaration("Table", new CodeAttributeArgument("Name", new CodePrimitiveExpression(table.Name))), }, }; WriteCustomTypes(_class, table); var havePrimaryKeys = table.Type.Columns.Any(c => c.IsPrimaryKey); if (havePrimaryKeys) { GenerateINotifyPropertyChanging(_class); GenerateINotifyPropertyChanged(_class); } // Implement Constructor var constructor = new CodeConstructor() { Attributes = MemberAttributes.Public }; // 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 = GetStorageFieldName(child); constructor.Statements.Add( new CodeAssignStatement( new CodeVariableReferenceExpression(entitySetMember), new CodeObjectCreateExpression( new CodeTypeReference("EntitySet", new CodeTypeReference(child.Type)), new CodeDelegateCreateExpression( new CodeTypeReference("Action", new CodeTypeReference(child.Type)), thisReference, child.Member + "_Attach"), new CodeDelegateCreateExpression( new CodeTypeReference("Action", new CodeTypeReference(child.Type)), thisReference, child.Member + "_Detach")))); } constructor.Statements.Add(new CodeExpressionStatement(new CodeMethodInvokeExpression(thisReference, "OnCreated"))); _class.Members.Add(constructor); if (Context.Parameters.GenerateEqualsHash) { GenerateEntityGetHashCodeAndEquals(_class, table); } GenerateExtensibilityDeclarations(_class, table); // todo: add these when the actually get called //partial void OnLoaded(); //partial void OnValidate(System.Data.Linq.ChangeAction action); // columns foreach (Column column in table.Type.Columns) { var relatedAssociations = from a in table.Type.Associations where a.IsForeignKey && a.TheseKeys.Contains(column.Name) select a; var type = ToCodeTypeReference(column); var columnMember = column.Member ?? column.Name; var field = new CodeMemberField(type, GetStorageFieldName(column)); _class.Members.Add(field); var fieldReference = new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), field.Name); var onChanging = GetChangingMethodName(columnMember); var onChanged = GetChangedMethodName(columnMember); var property = new CodeMemberProperty(); property.Type = type; property.Name = columnMember; property.Attributes = MemberAttributes.Public | MemberAttributes.Final; var defAttrValues = new ColumnAttribute(); var args = new List<CodeAttributeArgument>() { new CodeAttributeArgument("Storage", new CodePrimitiveExpression(GetStorageFieldName(column))), new CodeAttributeArgument("Name", new CodePrimitiveExpression(column.Name)), new CodeAttributeArgument("DbType", new CodePrimitiveExpression(column.DbType)), }; if (defAttrValues.IsPrimaryKey != column.IsPrimaryKey) args.Add(new CodeAttributeArgument("IsPrimaryKey", new CodePrimitiveExpression(column.IsPrimaryKey))); if (defAttrValues.IsDbGenerated != column.IsDbGenerated) args.Add(new CodeAttributeArgument("IsDbGenerated", new CodePrimitiveExpression(column.IsDbGenerated))); if (column.AutoSync != DbLinq.Schema.Dbml.AutoSync.Default) args.Add(new CodeAttributeArgument("AutoSync", new CodeFieldReferenceExpression(new CodeTypeReferenceExpression("AutoSync"), column.AutoSync.ToString()))); if (defAttrValues.CanBeNull != column.CanBeNull) args.Add(new CodeAttributeArgument("CanBeNull", new CodePrimitiveExpression(column.CanBeNull))); if (column.Expression != null) args.Add(new CodeAttributeArgument("Expression", new CodePrimitiveExpression(column.Expression))); property.CustomAttributes.Add( new CodeAttributeDeclaration("Column", args.ToArray())); property.CustomAttributes.Add(new CodeAttributeDeclaration("DebuggerNonUserCode")); property.GetStatements.Add(new CodeMethodReturnStatement(fieldReference)); var whenUpdating = new List<CodeStatement>( from assoc in relatedAssociations select (CodeStatement) new CodeConditionStatement( new CodePropertyReferenceExpression( new CodeVariableReferenceExpression(GetStorageFieldName(assoc)), "HasLoadedOrAssignedValue"), new CodeThrowExceptionStatement( new CodeObjectCreateExpression(typeof(System.Data.Linq.ForeignKeyReferenceAlreadyHasValueException))))); whenUpdating.Add( new CodeExpressionStatement(new CodeMethodInvokeExpression(thisReference, onChanging, new CodePropertySetValueReferenceExpression()))); if (havePrimaryKeys) whenUpdating.Add( new CodeExpressionStatement(new CodeMethodInvokeExpression(thisReference, "SendPropertyChanging"))); whenUpdating.Add( new CodeAssignStatement(fieldReference, new CodePropertySetValueReferenceExpression())); if (havePrimaryKeys) whenUpdating.Add( new CodeExpressionStatement(new CodeMethodInvokeExpression(thisReference, "SendPropertyChanged", new CodePrimitiveExpression(property.Name)))); whenUpdating.Add( new CodeExpressionStatement(new CodeMethodInvokeExpression(thisReference, onChanged))); var fieldType = TypeLoader.Load(column.Type); // This is needed for VB.NET generation; // int/string/etc. can use '<>' for comparison, but NOT arrays and other reference types. // arrays/etc. require the 'Is' operator, which is CodeBinaryOperatorType.IdentityEquality. // The VB IsNot operator is not exposed from CodeDom. // Thus, we need to special-case: if fieldType is a ref or nullable type, // generate '(field Is value) = false'; otherwise, // generate '(field <> value)' CodeBinaryOperatorExpression condition = fieldType.IsClass || fieldType.IsNullable() ? ValuesAreNotEqual_Ref(new CodeVariableReferenceExpression(field.Name), new CodePropertySetValueReferenceExpression()) : ValuesAreNotEqual(new CodeVariableReferenceExpression(field.Name), new CodePropertySetValueReferenceExpression()); property.SetStatements.Add(new CodeConditionStatement(condition, whenUpdating.ToArray())); _class.Members.Add(property); } GenerateEntityChildren(_class, table, database); GenerateEntityChildrenAttachment(_class, table, database); GenerateEntityParents(_class, table, database); return _class; }