/// <summary> /// Returns the reverse association /// </summary> /// <param name="schema"></param> /// <param name="association"></param> /// <returns></returns> public static Association GetReverseAssociation(this Database schema, Association association) { // we use Single() because we NEED the opposite association // (and it must be here var reverseTable = (from t in schema.Table where t.Type.Name == association.Type select t).Single(); // same thing for association var reverseAssociation = (from a in reverseTable.Type.Associations where a.Name == association.Name && a != association select a).Single(); return reverseAssociation; }
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 = names.ColumnsNames[tableName][columnName].PropertyName; //var reverseForeignKey = names.ColumnsNames[referencedTableName][referencedColumnName].PropertyName; 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.Cardinality = Cardinality.Many; // TODO: check this is the right direction (even if it appears to be useless) 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.Cardinality = Cardinality.One; //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; } }
/// <summary> /// Returns child detach method name /// </summary> /// <param name="child"></param> /// <returns></returns> protected virtual string GetChildDetachMethodName(Association child) { return GetChildMethodName(child, "Detach"); }
/// <summary> /// Returns event method name related to a child /// </summary> /// <param name="child"></param> /// <param name="method"></param> /// <returns></returns> protected virtual string GetChildMethodName(Association child, string method) { return string.Format("{0}_{1}", child.Member, method); }
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(); }
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(); }
static string GetStorageFieldName(Association association) { return association.Storage != null ? GetStorageFieldName(association.Storage) : "_" + CreateIdentifier(association.Member ?? association.Name); }
static MemberAttributes ToMemberAttributes(Association association) { MemberAttributes attrs = 0; if (!association.AccessModifierSpecified) attrs |= MemberAttributes.Public; else switch (association.AccessModifier) { case AccessModifier.Internal: attrs = MemberAttributes.Assembly; break; case AccessModifier.Private: attrs = MemberAttributes.Private; break; case AccessModifier.Protected: attrs = MemberAttributes.Family; break; case AccessModifier.ProtectedInternal: attrs = MemberAttributes.FamilyOrAssembly; break; case AccessModifier.Public: attrs = MemberAttributes.Public; break; default: throw new ArgumentOutOfRangeException("association", "Modifier value '" + association.AccessModifierSpecified + "' is an unsupported value."); } if (!association.ModifierSpecified) attrs |= MemberAttributes.Final; else switch (association.Modifier) { case MemberModifier.New: attrs |= MemberAttributes.New | MemberAttributes.Final; break; case MemberModifier.NewVirtual: attrs |= MemberAttributes.New; break; case MemberModifier.Override: attrs |= MemberAttributes.Override; break; case MemberModifier.Virtual: break; } return attrs; }