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