1 #region MIT license 2 // 3 // MIT license 4 // 5 // Copyright (c) 2007-2008 Jiri Moudry, Pascal Craponne 6 // 7 // Permission is hereby granted, free of charge, to any person obtaining a copy 8 // of this software and associated documentation files (the "Software"), to deal 9 // in the Software without restriction, including without limitation the rights 10 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 // copies of the Software, and to permit persons to whom the Software is 12 // furnished to do so, subject to the following conditions: 13 // 14 // The above copyright notice and this permission notice shall be included in 15 // all copies or substantial portions of the Software. 16 // 17 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 // THE SOFTWARE. 24 // 25 #endregion 26 27 using System.Data; 28 using System.Linq; 29 using DbLinq.Schema; 30 using DbLinq.Schema.Dbml; 31 using System.Text; 32 using System.Collections.Generic; 33 34 namespace DbLinq.Vendor.Implementation 35 { 36 partial class SchemaLoader 37 { 38 /// <summary> 39 /// Loads the constraints. 40 /// </summary> 41 /// <param name="schema">The schema.</param> 42 /// <param name="schemaName">Name of the schema.</param> 43 /// <param name="conn">The conn.</param> 44 /// <param name="nameFormat">The name format.</param> 45 /// <param name="names">The names.</param> LoadConstraints(Database schema, SchemaName schemaName, IDbConnection conn, NameFormat nameFormat, Names names)46 protected abstract void LoadConstraints(Database schema, SchemaName schemaName, IDbConnection conn, NameFormat nameFormat, Names names); 47 BuildForeignKey(IDictionary<string, ColumnName> table, string key)48 protected string BuildForeignKey(IDictionary<string, ColumnName> table, string key) 49 { 50 string[] keys = key.Split(','); 51 StringBuilder result = new StringBuilder(); 52 foreach (string lookupKey in keys) 53 { 54 if (result.Length != 0) 55 result.Append(','); 56 result.Append(table[lookupKey].PropertyName); 57 } 58 59 return result.ToString(); 60 } 61 62 /// <summary> 63 /// Loads the foreign key. 64 /// </summary> 65 /// <param name="schema">The schema.</param> 66 /// <param name="table">The table.</param> 67 /// <param name="columnName">Name of the column.</param> 68 /// <param name="tableName">Name of the table.</param> 69 /// <param name="tableSchema">The table schema.</param> 70 /// <param name="referencedColumnName">Name of the referenced column.</param> 71 /// <param name="referencedTableName">Name of the referenced table.</param> 72 /// <param name="referencedTableSchema">The referenced table schema.</param> 73 /// <param name="constraintName">Name of the constraint.</param> 74 /// <param name="nameFormat">The name format.</param> 75 /// <param name="names">The names.</param> LoadForeignKey(Database schema, Table table, string columnName, string tableName, string tableSchema, string referencedColumnName, string referencedTableName, string referencedTableSchema, string constraintName, NameFormat nameFormat, Names names)76 protected virtual void LoadForeignKey(Database schema, Table table, string columnName, string tableName, string tableSchema, 77 string referencedColumnName, string referencedTableName, string referencedTableSchema, 78 string constraintName, 79 NameFormat nameFormat, Names names) 80 { 81 var foreignKey = BuildForeignKey(names.ColumnsNames[tableName], columnName); 82 var reverseForeignKey = BuildForeignKey(names.ColumnsNames[referencedTableName], referencedColumnName); 83 84 var associationName = CreateAssociationName(tableName, tableSchema, 85 referencedTableName, referencedTableSchema, constraintName, foreignKey, nameFormat); 86 87 //both parent and child table get an [Association] 88 var assoc = new Association(); 89 assoc.IsForeignKey = true; 90 assoc.Name = constraintName; 91 assoc.Type = null; 92 assoc.ThisKey = foreignKey; 93 assoc.OtherKey = reverseForeignKey; 94 assoc.Member = associationName.ManyToOneMemberName; 95 assoc.CardinalitySpecified = false; 96 // TODO: generate assoc.Cardinality? 97 table.Type.Associations.Add(assoc); 98 99 //and insert the reverse association: 100 var reverseAssociation = new Association(); 101 reverseAssociation.Name = constraintName; 102 reverseAssociation.Type = table.Type.Name; 103 reverseAssociation.Member = associationName.OneToManyMemberName; 104 reverseAssociation.CardinalitySpecified = false; 105 // TODO: generate reverseAssociation.Cardinality? 106 reverseAssociation.ThisKey = reverseForeignKey; 107 reverseAssociation.OtherKey = foreignKey; 108 reverseAssociation.DeleteRule = "NO ACTION"; 109 110 string referencedFullDbName = GetFullDbName(referencedTableName, referencedTableSchema); 111 var referencedTable = schema.Tables.FirstOrDefault(t => referencedFullDbName == t.Name); 112 if (referencedTable == null) 113 { 114 //try case-insensitive match 115 //reason: MySql's Key_Column_Usage table contains both 'Northwind' and 'northwind' 116 referencedTable = schema.Tables.FirstOrDefault(t => referencedFullDbName.ToLower() == t.Name.ToLower()); 117 } 118 119 if (referencedTable == null) 120 { 121 ReportForeignKeyError(schema, referencedFullDbName); 122 } 123 else 124 { 125 referencedTable.Type.Associations.Add(reverseAssociation); 126 assoc.Type = referencedTable.Type.Name; 127 } 128 } 129 130 /// <summary> 131 /// Reports a foreign key error. 132 /// </summary> 133 /// <param name="schema">The schema.</param> 134 /// <param name="referencedTableFull">The referenced table full.</param> ReportForeignKeyError(Database schema, string referencedTableFull)135 private void ReportForeignKeyError(Database schema, string referencedTableFull) 136 { 137 var tablesMap = schema.Tables.ToDictionary(t => t.Name.ToLower()); 138 var referencedTableFullL = referencedTableFull.ToLower(); 139 140 string msg = "ERROR L91: parent table not found: " + referencedTableFull; 141 Table matchedTable; 142 if (tablesMap.TryGetValue(referencedTableFullL, out matchedTable)) 143 { 144 //case problems arise from various reasons, 145 //e.g. different capitalization on WIndows vs Linux, 146 //bugs in DbLinq etc 147 msg += " - however, schema lists a table spelled as " + matchedTable.Name; 148 } 149 WriteErrorLine(msg); 150 } 151 } 152 } 153