/src/NHibernate.Tool.hbm2net/src/NHibernate.Tool.Db2hbm/KeyAwareMetadataStrategy.cs

https://github.com/elevate/nhcontrib · C# · 255 lines · 235 code · 15 blank · 5 comment · 21 complexity · c953a8f6d1c7c29abb852f1e000003dd MD5 · raw file

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. using System.Data;
  6. using NHibernate.Dialect.Schema;
  7. using log4net;
  8. namespace NHibernate.Tool.Db2hbm
  9. {
  10. ///<author>
  11. /// Felice Pollano (felice@felicepollano.com)
  12. ///</author>
  13. public abstract class KeyAwareMetadataStrategy:IMetadataStrategy
  14. {
  15. GenerationContext currentContext;
  16. Dictionary<string, Dictionary<string, IForeignKeyColumnInfo[]>> fkForTables;
  17. public ILog logger { protected get; set; }
  18. const string CONSTRAINT_NAME = "CONSTRAINT_NAME";
  19. const string ISKEY = "IsKey";
  20. const string ISIDENTITY = "IsIdentity";
  21. const string COLNAME = "ColumnName";
  22. protected IDictionary<string, Dictionary<string, IForeignKeyColumnInfo[]>> FkForTables
  23. {
  24. get { return fkForTables; }
  25. }
  26. #region IMetadataStrategy Members
  27. public void Process(GenerationContext context)
  28. {
  29. currentContext = context;
  30. if (context["FKMAP"] == null)
  31. {
  32. fkForTables = new Dictionary<string, Dictionary<string, IForeignKeyColumnInfo[]>>();
  33. try
  34. {
  35. CollectForeignKeysFromSchema();
  36. }
  37. catch (Exception e)
  38. {
  39. logger.Warn("Retrieving foreign keys info from schema failed.", e);
  40. }
  41. CollectForeignKeyFromConfig();
  42. context["FKMAP"] = fkForTables;
  43. }
  44. else
  45. {
  46. fkForTables = context["FKMAP"] as Dictionary<string, Dictionary<string, IForeignKeyColumnInfo[]>>;
  47. }
  48. OnProcess(context);
  49. }
  50. #endregion
  51. protected abstract void OnProcess(GenerationContext context);
  52. protected DataTable GetCompleteColumnSchema(ITableMetadata tableMetaData)
  53. {
  54. string key = string.Format("COLUMN.SCHEMA.{0}.{1}.{2}", tableMetaData.Catalog, tableMetaData.Schema, tableMetaData.Name);
  55. if (null == currentContext[key])
  56. {
  57. DataTable dt = currentContext.Dialect.GetCompleteColumnSchema(currentContext.Connection, tableMetaData.Schema, tableMetaData.Name);
  58. currentContext[key] = dt;
  59. return dt;
  60. }
  61. else
  62. {
  63. return currentContext[key] as DataTable;
  64. }
  65. }
  66. protected virtual void CollectForeignKeysFromSchema()
  67. {
  68. IForeignKeyCrawler crawler = ForeignKeyCrawlersRegistar.GetForDialect(currentContext.Dialect.GetType());
  69. foreach (DataRow t in currentContext.FilteredTables)
  70. {
  71. var tableMeta = currentContext.Schema.GetTableMetadata(t, true);
  72. var keys = currentContext.Schema.GetForeignKeys(tableMeta.Catalog, tableMeta.Schema, tableMeta.Name);
  73. int nameIndex = keys.Columns.IndexOf(CONSTRAINT_NAME);
  74. foreach (DataRow key in keys.Rows)
  75. {
  76. var keyMeta = tableMeta.GetForeignKeyMetadata(key.ItemArray[nameIndex].ToString());
  77. var keyColumns = crawler.GetForeignKeyColumns(currentContext.Connection, keyMeta.Name, tableMeta.Catalog, tableMeta.Schema);
  78. AddFKColumnInfo(tableMeta.Name, keyMeta.Name, keyColumns);
  79. }
  80. }
  81. }
  82. private void CollectForeignKeyFromConfig()
  83. {
  84. foreach (DataRow t in currentContext.FilteredTables)
  85. {
  86. var tableMeta = currentContext.Schema.GetTableMetadata(t, true);
  87. if (currentContext.TableExceptions.HasException(tableMeta.Name, tableMeta.Catalog, tableMeta.Schema))
  88. {
  89. var tablecfg = currentContext.TableExceptions.GetTableException(tableMeta.Name, tableMeta.Catalog, tableMeta.Schema);
  90. if (null != tablecfg.foreignkey)
  91. {
  92. foreach (var fk in tablecfg.foreignkey)
  93. {
  94. AddFKColumnInfo(tableMeta.Name, fk.constraintname, GetColumnInfoFromConfig(fk, tableMeta));
  95. }
  96. }
  97. }
  98. }
  99. }
  100. protected void AddFKColumnInfo(string tablename, string constraint, IForeignKeyColumnInfo[] keyColumns)
  101. {
  102. if (!fkForTables.ContainsKey(tablename))
  103. fkForTables[tablename] = new Dictionary<string, IForeignKeyColumnInfo[]>();
  104. // need a merge...
  105. if (!fkForTables[tablename].ContainsKey(constraint))
  106. {
  107. fkForTables[tablename][constraint] = keyColumns;
  108. }
  109. else
  110. {
  111. List<IForeignKeyColumnInfo> orig = new List<IForeignKeyColumnInfo>();
  112. orig.AddRange(fkForTables[tablename][constraint]);
  113. orig.AddRange(keyColumns);
  114. fkForTables[tablename][constraint] = orig.ToArray();
  115. }
  116. }
  117. private IForeignKeyColumnInfo[] GetColumnInfoFromConfig(cfg.db2hbmconfTableForeignkey fk, ITableMetadata metaData)
  118. {
  119. List<IForeignKeyColumnInfo> fks = new List<IForeignKeyColumnInfo>();
  120. foreach (var ci in fk.columnref)
  121. {
  122. fks.Add(new ConfiguredForeignKeyColumnInfo(ci, fk, metaData));
  123. }
  124. return fks.ToArray();
  125. }
  126. protected IColumnMetadata[] GetKeyColumns(NHibernate.Dialect.Schema.ITableMetadata tableMetaData)
  127. {
  128. string key = string.Format("PK_{0}.{1}.{2}", tableMetaData.Catalog, tableMetaData.Schema, tableMetaData.Name);
  129. if (currentContext[key] == null)
  130. {
  131. if (currentContext.TableExceptions.HasException(tableMetaData.Name, tableMetaData.Catalog, tableMetaData.Schema))
  132. {
  133. cfg.db2hbmconfTable exc = currentContext.TableExceptions.GetTableException(tableMetaData.Name, tableMetaData.Catalog, tableMetaData.Schema);
  134. if (exc.primarykey != null)
  135. {
  136. if (exc.primarykey.keycolumn != null && exc.primarykey.keycolumn.Length > 0)
  137. {
  138. logger.Info(string.Format("table {0}: using configured primary key def", tableMetaData.Name));
  139. try
  140. {
  141. IColumnMetadata[] res = exc.primarykey.keycolumn.Select(q => tableMetaData.GetColumnMetadata(q.name)).Where(q => q != null).ToArray();
  142. currentContext[key] = res;
  143. return res;
  144. }
  145. catch (Exception e)
  146. {
  147. logger.Error("Can't obtain metadata for configured primary key columns.", e);
  148. throw e;
  149. }
  150. }
  151. }
  152. }
  153. //try to found the primary key from the schema
  154. try
  155. {
  156. var dt = currentContext.Dialect.GetCompleteColumnSchema(currentContext.Connection, tableMetaData.Schema, tableMetaData.Name);
  157. int isKey = dt.Columns.IndexOf(ISKEY);
  158. int colName = dt.Columns.IndexOf(COLNAME);
  159. List<IColumnMetadata> cols = new List<IColumnMetadata>();
  160. foreach (DataRow dr in dt.Rows)
  161. {
  162. if ((bool)dr.ItemArray[isKey])
  163. {
  164. var cmeta = tableMetaData.GetColumnMetadata(dr.ItemArray[colName].ToString());
  165. if( null != cmeta )
  166. cols.Add(cmeta);
  167. }
  168. }
  169. currentContext[key] = cols.ToArray();
  170. return currentContext[key] as IColumnMetadata[];
  171. }
  172. catch (Exception e)
  173. {
  174. logger.Error("Can't obtain primary key metadata from schema.If database does not support key schema information, please consider to use configuration in order to provide keys", e);
  175. throw e;
  176. }
  177. }
  178. else
  179. {
  180. return currentContext[key] as IColumnMetadata[];
  181. }
  182. }
  183. class ConfiguredForeignKeyColumnInfo : IForeignKeyColumnInfo
  184. {
  185. public ConfiguredForeignKeyColumnInfo(cfg.db2hbmconfTableForeignkeyColumnref cref, cfg.db2hbmconfTableForeignkey fk, ITableMetadata metaData)
  186. {
  187. PrimaryKeyTableName = fk.foreigntable;
  188. PrimaryKeyTableCatalog = fk.foreigncatalog;
  189. PrimaryKeyTableSchema = fk.foreignschema;
  190. PrimaryKeyColumnName = cref.foreigncolumn;
  191. ForeignKeyColumnName = cref.localcolumn;
  192. ForeignKeyTableCatalog = metaData.Catalog;
  193. ForeignKeyTableSchema = metaData.Schema;
  194. ForeignKeyTableName = metaData.Name;
  195. }
  196. #region IForeignKeyColumnInfo Members
  197. public string PrimaryKeyColumnName
  198. {
  199. get;
  200. private set;
  201. }
  202. public string PrimaryKeyTableName
  203. {
  204. get;
  205. private set;
  206. }
  207. public string PrimaryKeyTableSchema
  208. {
  209. get;
  210. private set;
  211. }
  212. public string PrimaryKeyTableCatalog
  213. {
  214. get;
  215. private set;
  216. }
  217. public string ForeignKeyColumnName
  218. {
  219. get;
  220. private set;
  221. }
  222. public string ForeignKeyTableName
  223. {
  224. get;
  225. private set;
  226. }
  227. public string ForeignKeyTableSchema
  228. {
  229. get;
  230. private set;
  231. }
  232. public string ForeignKeyTableCatalog
  233. {
  234. get;
  235. private set;
  236. }
  237. #endregion
  238. }
  239. }
  240. }