PageRenderTime 66ms CodeModel.GetById 33ms RepoModel.GetById 0ms app.codeStats 0ms

/mcs/class/System.Data.Linq/src/DbLinq/Vendor/Implementation/SchemaLoader.cs

https://github.com/ztfuqingvip/mono
C# | 453 lines | 252 code | 45 blank | 156 comment | 49 complexity | d463806215a1d766ac5237d6d5e67f00 MD5 | raw file
Possible License(s): GPL-2.0, Unlicense, MPL-2.0-no-copyleft-exception, CC-BY-SA-3.0
  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. using System;
  27. using System.Collections.Generic;
  28. using System.Data;
  29. using System.IO;
  30. using System.Linq;
  31. #if MONO_STRICT
  32. using System.Data.Linq;
  33. #else
  34. using DbLinq.Data.Linq;
  35. #endif
  36. using DbLinq.Factory;
  37. using DbLinq.Schema;
  38. using DbLinq.Schema.Dbml;
  39. using System.Text.RegularExpressions;
  40. namespace DbLinq.Vendor.Implementation
  41. {
  42. #if !MONO_STRICT
  43. public
  44. #endif
  45. abstract partial class SchemaLoader : ISchemaLoader
  46. {
  47. /// <summary>
  48. /// Underlying vendor
  49. /// </summary>
  50. /// <value></value>
  51. public abstract IVendor Vendor { get; set; }
  52. /// <summary>
  53. /// Connection used to read schema
  54. /// </summary>
  55. /// <value></value>
  56. public IDbConnection Connection { get; set; }
  57. /// <summary>
  58. /// Gets or sets the name formatter.
  59. /// </summary>
  60. /// <value>The name formatter.</value>
  61. public INameFormatter NameFormatter { get; set; }
  62. private TextWriter log;
  63. /// <summary>
  64. /// Log output
  65. /// </summary>
  66. public TextWriter Log
  67. {
  68. get { return log ?? Console.Out; }
  69. set { log = value; }
  70. }
  71. /// <summary>
  72. /// Loads database schema
  73. /// </summary>
  74. /// <param name="databaseName"></param>
  75. /// <param name="nameAliases"></param>
  76. /// <param name="nameFormat"></param>
  77. /// <param name="loadStoredProcedures"></param>
  78. /// <param name="contextNamespace"></param>
  79. /// <param name="entityNamespace"></param>
  80. /// <returns></returns>
  81. public virtual Database Load(string databaseName, INameAliases nameAliases, NameFormat nameFormat,
  82. bool loadStoredProcedures, string contextNamespace, string entityNamespace)
  83. {
  84. // check if connection is open. Note: we may use something more flexible
  85. if (Connection.State != ConnectionState.Open)
  86. Connection.Open();
  87. // get the database name. If we don't have one, take it from connection string...
  88. if (string.IsNullOrEmpty(databaseName))
  89. databaseName = Connection.Database;
  90. // ... and if connection string doesn't provide a name, then throw an error
  91. if (string.IsNullOrEmpty(databaseName))
  92. throw new ArgumentException("A database name is required. Please specify /database=<databaseName>");
  93. databaseName = GetDatabaseNameAliased(databaseName, nameAliases);
  94. var schemaName = NameFormatter.GetSchemaName(databaseName, GetExtraction(databaseName), nameFormat);
  95. var names = new Names();
  96. var schema = new Database
  97. {
  98. Name = schemaName.DbName,
  99. Class = GetRuntimeClassName(schemaName.ClassName, nameAliases),
  100. BaseType = typeof(DataContext).FullName,
  101. ContextNamespace = contextNamespace,
  102. EntityNamespace = entityNamespace,
  103. };
  104. // order is important, we must have:
  105. // 1. tables
  106. // 2. columns
  107. // 3. constraints
  108. LoadTables(schema, schemaName, Connection, nameAliases, nameFormat, names);
  109. LoadColumns(schema, schemaName, Connection, nameAliases, nameFormat, names);
  110. CheckColumnsName(schema);
  111. LoadConstraints(schema, schemaName, Connection, nameFormat, names);
  112. CheckConstraintsName(schema);
  113. if (loadStoredProcedures)
  114. LoadStoredProcedures(schema, schemaName, Connection, nameFormat);
  115. // names aren't checked here anymore, because this confuses DBML editor.
  116. // they will (for now) be checked before .cs generation
  117. // in the end, when probably will end up in mapping source (or somewhere around)
  118. //CheckNamesSafety(schema);
  119. // generate backing fields name (since we have here correct names)
  120. GenerateStorageAndMemberFields(schema);
  121. return schema;
  122. }
  123. /// <summary>
  124. /// Gets a usable name for the database.
  125. /// </summary>
  126. /// <param name="databaseName">Name of the database.</param>
  127. /// <returns></returns>
  128. protected virtual string GetDatabaseName(string databaseName)
  129. {
  130. return databaseName;
  131. }
  132. protected virtual string GetDatabaseNameAliased(string databaseName, INameAliases nameAliases)
  133. {
  134. string databaseNameAliased = nameAliases != null ? nameAliases.GetDatabaseNameAlias(databaseName) : null;
  135. return (databaseNameAliased != null) ? databaseNameAliased : GetDatabaseName(databaseName);
  136. }
  137. /// <summary>
  138. /// Gets a usable name for the database class.
  139. /// </summary>
  140. /// <param name="databaseName">Name of the clas.</param>
  141. /// <returns></returns>
  142. protected virtual string GetRuntimeClassName(string className, INameAliases nameAliases)
  143. {
  144. string classNameAliased = nameAliases != null ? nameAliases.GetClassNameAlias(className) : null;
  145. return (classNameAliased != null) ? classNameAliased : className;
  146. }
  147. /// <summary>
  148. /// Writes an error line.
  149. /// </summary>
  150. /// <param name="format">The format.</param>
  151. /// <param name="arg">The arg.</param>
  152. protected void WriteErrorLine(string format, params object[] arg)
  153. {
  154. var o = Log;
  155. if (o == Console.Out)
  156. o = Console.Error;
  157. o.WriteLine(format, arg);
  158. }
  159. protected SchemaLoader()
  160. {
  161. NameFormatter = ObjectFactory.Create<INameFormatter>(); // the Pluralize property is set dynamically, so no singleton
  162. }
  163. /// <summary>
  164. /// Gets the extraction type from a columnname.
  165. /// </summary>
  166. /// <param name="dbColumnName">Name of the db column.</param>
  167. /// <returns></returns>
  168. protected virtual WordsExtraction GetExtraction(string dbColumnName)
  169. {
  170. bool isMixedCase = dbColumnName != dbColumnName.ToLower() && dbColumnName != dbColumnName.ToUpper();
  171. return isMixedCase ? WordsExtraction.FromCase : WordsExtraction.FromDictionary;
  172. }
  173. /// <summary>
  174. /// Gets the full name of a name and schema.
  175. /// </summary>
  176. /// <param name="dbName">Name of the db.</param>
  177. /// <param name="dbSchema">The db schema.</param>
  178. /// <returns></returns>
  179. protected virtual string GetFullDbName(string dbName, string dbSchema)
  180. {
  181. string fullDbName;
  182. if (dbSchema == null)
  183. fullDbName = dbName;
  184. else
  185. fullDbName = string.Format("{0}.{1}", dbSchema, dbName);
  186. return fullDbName;
  187. }
  188. /// <summary>
  189. /// Creates the name of the table given a name and schema
  190. /// </summary>
  191. /// <param name="dbTableName">Name of the db table.</param>
  192. /// <param name="dbSchema">The db schema.</param>
  193. /// <param name="nameAliases">The name aliases.</param>
  194. /// <param name="nameFormat">The name format.</param>
  195. /// <param name="extraction">The extraction.</param>
  196. /// <returns></returns>
  197. protected virtual TableName CreateTableName(string dbTableName, string dbSchema, INameAliases nameAliases, NameFormat nameFormat, WordsExtraction extraction)
  198. {
  199. // if we have an alias, use it, and don't try to analyze it (a human probably already did the job)
  200. var tableTypeAlias = nameAliases != null ? nameAliases.GetTableTypeAlias(dbTableName, dbSchema) : null;
  201. if (tableTypeAlias != null)
  202. extraction = WordsExtraction.None;
  203. else
  204. tableTypeAlias = dbTableName;
  205. var tableName = NameFormatter.GetTableName(tableTypeAlias, extraction, nameFormat);
  206. // alias for member
  207. var tableMemberAlias = nameAliases != null ? nameAliases.GetTableMemberAlias(dbTableName, dbSchema) : null;
  208. if (tableMemberAlias != null)
  209. tableName.MemberName = tableMemberAlias;
  210. tableName.DbName = GetFullDbName(dbTableName, dbSchema);
  211. return tableName;
  212. }
  213. protected virtual TableName CreateTableName(string dbTableName, string dbSchema, INameAliases nameAliases, NameFormat nameFormat)
  214. {
  215. return CreateTableName(dbTableName, dbSchema, nameAliases, nameFormat, GetExtraction(dbTableName));
  216. }
  217. Regex startsWithNumber = new Regex(@"^\d", RegexOptions.Compiled);
  218. /// <summary>
  219. /// Creates the name of the column.
  220. /// </summary>
  221. /// <param name="dbColumnName">Name of the db column.</param>
  222. /// <param name="dbTableName">Name of the db table.</param>
  223. /// <param name="dbSchema">The db schema.</param>
  224. /// <param name="nameAliases">The name aliases.</param>
  225. /// <param name="nameFormat">The name format.</param>
  226. /// <returns></returns>
  227. protected virtual ColumnName CreateColumnName(string dbColumnName, string dbTableName, string dbSchema, INameAliases nameAliases, NameFormat nameFormat)
  228. {
  229. var columnNameAlias = nameAliases != null ? nameAliases.GetColumnMemberAlias(dbColumnName, dbTableName, dbSchema) : null;
  230. WordsExtraction extraction;
  231. if (columnNameAlias != null)
  232. {
  233. extraction = WordsExtraction.None;
  234. }
  235. else
  236. {
  237. extraction = GetExtraction(dbColumnName);
  238. columnNameAlias = dbColumnName;
  239. }
  240. var columnName = NameFormatter.GetColumnName(columnNameAlias, extraction, nameFormat);
  241. // The member name can not be the same as the class
  242. // we add a "1" (just like SqlMetal does)
  243. var tableName = CreateTableName(dbTableName, dbSchema, nameAliases, nameFormat);
  244. if (columnName.PropertyName == tableName.ClassName)
  245. columnName.PropertyName = columnName.PropertyName + "1";
  246. if (startsWithNumber.IsMatch(columnName.PropertyName))
  247. columnName.PropertyName = "_" + columnName.PropertyName;
  248. columnName.DbName = dbColumnName;
  249. return columnName;
  250. }
  251. /// <summary>
  252. /// Creates the name of the procedure.
  253. /// </summary>
  254. /// <param name="dbProcedureName">Name of the db procedure.</param>
  255. /// <param name="dbSchema">The db schema.</param>
  256. /// <param name="nameFormat">The name format.</param>
  257. /// <returns></returns>
  258. protected virtual ProcedureName CreateProcedureName(string dbProcedureName, string dbSchema, NameFormat nameFormat)
  259. {
  260. var procedureName = NameFormatter.GetProcedureName(dbProcedureName, GetExtraction(dbProcedureName), nameFormat);
  261. procedureName.DbName = GetFullDbName(dbProcedureName, dbSchema);
  262. return procedureName;
  263. }
  264. /// <summary>
  265. /// Creates the name of the association.
  266. /// </summary>
  267. /// <param name="dbManyName">Name of the db many.</param>
  268. /// <param name="dbManySchema">The db many schema.</param>
  269. /// <param name="dbOneName">Name of the db one.</param>
  270. /// <param name="dbOneSchema">The db one schema.</param>
  271. /// <param name="dbConstraintName">Name of the db constraint.</param>
  272. /// <param name="foreignKeyName">Name of the foreign key.</param>
  273. /// <param name="nameFormat">The name format.</param>
  274. /// <returns></returns>
  275. protected virtual AssociationName CreateAssociationName(string dbManyName, string dbManySchema,
  276. string dbOneName, string dbOneSchema, string dbConstraintName, string foreignKeyName, NameFormat nameFormat)
  277. {
  278. var associationName = NameFormatter.GetAssociationName(dbManyName, dbOneName,
  279. dbConstraintName, foreignKeyName, GetExtraction(dbManyName), nameFormat);
  280. associationName.DbName = GetFullDbName(dbManyName, dbManySchema);
  281. return associationName;
  282. }
  283. /// <summary>
  284. /// Creates the name of the schema.
  285. /// </summary>
  286. /// <param name="databaseName">Name of the database.</param>
  287. /// <param name="connection">The connection.</param>
  288. /// <param name="nameFormat">The name format.</param>
  289. /// <returns></returns>
  290. protected virtual SchemaName CreateSchemaName(string databaseName, IDbConnection connection, NameFormat nameFormat)
  291. {
  292. if (string.IsNullOrEmpty(databaseName))
  293. {
  294. databaseName = connection.Database;
  295. if (string.IsNullOrEmpty(databaseName))
  296. throw new ArgumentException("Could not deduce database name from connection string. Please specify /database=<databaseName>");
  297. }
  298. return NameFormatter.GetSchemaName(databaseName, GetExtraction(databaseName), nameFormat);
  299. }
  300. protected virtual ParameterName CreateParameterName(string dbParameterName, NameFormat nameFormat)
  301. {
  302. var parameterName = NameFormatter.GetParameterName(dbParameterName, GetExtraction(dbParameterName), nameFormat);
  303. return parameterName;
  304. }
  305. protected class Names
  306. {
  307. public IDictionary<string, TableName> TablesNames = new Dictionary<string, TableName>();
  308. public IDictionary<string, IDictionary<string, ColumnName>> ColumnsNames = new Dictionary<string, IDictionary<string, ColumnName>>();
  309. public void AddColumn(string dbTableName, ColumnName columnName)
  310. {
  311. IDictionary<string, ColumnName> columns;
  312. if (!ColumnsNames.TryGetValue(dbTableName, out columns))
  313. {
  314. columns = new Dictionary<string, ColumnName>();
  315. ColumnsNames[dbTableName] = columns;
  316. }
  317. columns[columnName.DbName] = columnName;
  318. }
  319. }
  320. /// <summary>
  321. /// Loads the tables in the given schema.
  322. /// </summary>
  323. /// <param name="schema">The schema.</param>
  324. /// <param name="schemaName">Name of the schema.</param>
  325. /// <param name="conn">The conn.</param>
  326. /// <param name="nameAliases">The name aliases.</param>
  327. /// <param name="nameFormat">The name format.</param>
  328. /// <param name="names">The names.</param>
  329. protected virtual void LoadTables(Database schema, SchemaName schemaName, IDbConnection conn, INameAliases nameAliases, NameFormat nameFormat, Names names)
  330. {
  331. var tables = ReadTables(conn, schemaName.DbName);
  332. foreach (var row in tables)
  333. {
  334. var tableName = CreateTableName(row.Name, row.Schema, nameAliases, nameFormat);
  335. names.TablesNames[tableName.DbName] = tableName;
  336. var table = new Table();
  337. table.Name = tableName.DbName;
  338. table.Member = tableName.MemberName;
  339. table.Type.Name = tableName.ClassName;
  340. schema.Tables.Add(table);
  341. }
  342. }
  343. /// <summary>
  344. /// Loads the columns.
  345. /// </summary>
  346. /// <param name="schema">The schema.</param>
  347. /// <param name="schemaName">Name of the schema.</param>
  348. /// <param name="conn">The conn.</param>
  349. /// <param name="nameAliases">The name aliases.</param>
  350. /// <param name="nameFormat">The name format.</param>
  351. /// <param name="names">The names.</param>
  352. protected void LoadColumns(Database schema, SchemaName schemaName, IDbConnection conn, INameAliases nameAliases, NameFormat nameFormat, Names names)
  353. {
  354. var columnRows = ReadColumns(conn, schemaName.DbName);
  355. foreach (var columnRow in columnRows)
  356. {
  357. var columnName = CreateColumnName(columnRow.ColumnName, columnRow.TableName, columnRow.TableSchema, nameAliases, nameFormat);
  358. names.AddColumn(columnRow.TableName, columnName);
  359. //find which table this column belongs to
  360. string fullColumnDbName = GetFullDbName(columnRow.TableName, columnRow.TableSchema);
  361. DbLinq.Schema.Dbml.Table tableSchema = schema.Tables.FirstOrDefault(tblSchema => fullColumnDbName == tblSchema.Name);
  362. if (tableSchema == null)
  363. {
  364. WriteErrorLine("ERROR L46: Table '" + columnRow.TableName + "' not found for column " + columnRow.ColumnName);
  365. continue;
  366. }
  367. var column = new Column();
  368. column.Name = columnName.DbName;
  369. column.Member = columnName.PropertyName;
  370. column.DbType = columnRow.FullType;
  371. if (columnRow.PrimaryKey.HasValue)
  372. column.IsPrimaryKey = columnRow.PrimaryKey.Value;
  373. bool? generated = (nameAliases != null) ? nameAliases.GetColumnGenerated(columnRow.ColumnName, columnRow.TableName, columnRow.TableSchema) : null;
  374. if (!generated.HasValue)
  375. generated = columnRow.Generated;
  376. if (generated.HasValue)
  377. column.IsDbGenerated = generated.Value;
  378. AutoSync? autoSync = (nameAliases != null) ? nameAliases.GetColumnAutoSync(columnRow.ColumnName, columnRow.TableName, columnRow.TableSchema) : null;
  379. if (autoSync.HasValue)
  380. column.AutoSync = autoSync.Value;
  381. // the Expression can originate from two sources:
  382. // 1. DefaultValue
  383. // 2. Expression
  384. // we use any valid source (we can't have both)
  385. if (column.IsDbGenerated && columnRow.DefaultValue != null)
  386. column.Expression = columnRow.DefaultValue;
  387. column.CanBeNull = columnRow.Nullable;
  388. string columnTypeAlias = nameAliases != null ? nameAliases.GetColumnForcedType(columnRow.ColumnName, columnRow.TableName, columnRow.TableSchema) : null;
  389. var columnType = MapDbType(columnName.DbName, columnRow);
  390. var columnEnumType = columnType as EnumType;
  391. if (columnEnumType != null)
  392. {
  393. var enumType = column.SetExtendedTypeAsEnumType();
  394. enumType.Name = columnEnumType.Name;
  395. foreach (KeyValuePair<string, int> enumValue in columnEnumType.EnumValues)
  396. {
  397. enumType[enumValue.Key] = enumValue.Value;
  398. }
  399. }
  400. else if (columnTypeAlias != null)
  401. column.Type = columnTypeAlias;
  402. else
  403. column.Type = columnType.ToString();
  404. tableSchema.Type.Columns.Add(column);
  405. }
  406. }
  407. }
  408. }