// // // // // $Revision: 1766 $ // /* * User: Dickon Field * Date: 12/06/2006 * Time: 06:25 */ using System; using System.Windows.Forms; using System.Data; using System.Data.Common; using System.Collections.Generic; using ICSharpCode.Core; using ICSharpCode.SharpDevelop.Gui; using SharpDbTools.Data; namespace SharpDbTools.Forms { /// /// Enables a user to browse metadata associated with a db server and to open resources /// referenced therein. The intention is to extend this to other server processes over /// time. /// public class ServerBrowserTool : AbstractPadContent { Panel ctl; /// /// ServerBrowserTool hosts one or more TreeViews providing views of types /// of server. Currently it shows only relational database servers. /// public ServerBrowserTool() { LoggingService.Debug("Loading ServerBrowserTool"); ServerToolTreeView dbTree = new ServerToolTreeView(); dbTree.Dock = DockStyle.Fill; ctl = new Panel(); ctl.Controls.Add(dbTree); } /// /// The representing the pad /// public override Control Control { get { return ctl; } } /// /// Rebuildes the pad /// public override void RedrawContent() { // TODO: Rebuild the whole pad control here, renew all resource strings whatever // Note that you do not need to recreate the control. } /// /// Cleans up all used resources /// public override void Dispose() { ctl.Dispose(); } } class ServerToolTreeView : TreeView { public ServerToolTreeView(): base() { // this is the wrong place for this, but lets give it a go... DbModelInfoService.LoadNamesFromFiles(); Rebuild(); } /// /// Rebuilds the database connection tree. /// Should only be called from a delegate via Invoke or BeginInvoke. /// public void Rebuild() { this.BeginUpdate(); // TODO: put the Rebuild... behaviour into a view builder; TreeNode dbNode = null; TreeNode[] dbNodes = this.Nodes.Find("DatabaseExplorer", true); // lets assume there is only one with this above name if (dbNodes.Length == 0) { LoggingService.Debug("could not find DatabaseExplorer Node, so creating it now"); dbNode = new TreeNode(); dbNode.Text = "Database Explorer"; dbNode.Name = "DatabaseExplorer"; this.Nodes.Add(dbNode); } else { dbNode = dbNodes[0]; } // create the context menu for the database server node ContextMenuStrip cMenu = new ContextMenuStrip(); ToolStripMenuItem addConnectionMenuItem = new ToolStripMenuItem("Add Connection"); addConnectionMenuItem.Click += new EventHandler(AddDbConnectionClickHandler); ToolStripMenuItem deleteConnectionMenuItem = new ToolStripMenuItem("Delete Connection"); deleteConnectionMenuItem.Click += new EventHandler(DeleteDbConnectionClickHandler); ToolStripMenuItem saveMetadataMenuItem = new ToolStripMenuItem("Save All"); saveMetadataMenuItem.Click += new EventHandler(SaveDbModelInfoClickHandler); cMenu.Items.AddRange(new ToolStripMenuItem[] { addConnectionMenuItem, deleteConnectionMenuItem, saveMetadataMenuItem } ); dbNode.ContextMenuStrip = cMenu; // Rebuild each of the root nodes in the ServerToolTreeView dbNode.Nodes.Clear(); foreach (string name in DbModelInfoService.Names) { TreeNode dbModelInfoNode = CreateDbModelInfoNode(name); TreeNode connectionNode = CreateConnectionPropertiesNode(name); TreeNode metadataNode = CreateMetaDataNode(name); dbModelInfoNode.Nodes.Add(connectionNode); dbModelInfoNode.Nodes.Add(metadataNode); dbNode.Nodes.Add(dbModelInfoNode); } this.EndUpdate(); } public TreeNode CreateDbModelInfoNode(string name) { TreeNode treeNode = new TreeNode(name); treeNode.Tag = "ConnectionRoot"; // create and add the menustrip for this node NodeAwareContextMenuStrip cMenu = new NodeAwareContextMenuStrip(treeNode); // create menu items ToolStripMenuItem setConnectionStringMenuItem = new ToolStripMenuItem("Set Connection String"); setConnectionStringMenuItem.Click += new EventHandler(SetConnectionStringOnDbModelInfoClickHandler); ToolStripMenuItem loadMetadataFromConnectionMenuItem = new ToolStripMenuItem("Load Metadata from Connection"); loadMetadataFromConnectionMenuItem.Click += new EventHandler(LoadMetadataFromConnectionClickHandler); ToolStripMenuItem loadMetadataFromFileMenuItem = new ToolStripMenuItem("Load Metadata from File"); loadMetadataFromFileMenuItem.Click += new EventHandler(LoadMetadataFromFileClickHandler); cMenu.Items.AddRange(new ToolStripMenuItem[] { setConnectionStringMenuItem, loadMetadataFromConnectionMenuItem, loadMetadataFromFileMenuItem }); treeNode.ContextMenuStrip = cMenu; return treeNode; } /// /// /// /// /// a TreeNode representation of the connection properties /// of the connection public TreeNode CreateConnectionPropertiesNode(string name) { // create sub TreeNodes for the connection string and invariant name if they exist DbModelInfo modelInfo = DbModelInfoService.GetDbModelInfo(name); if (name == null) throw new KeyNotFoundException(); string connectionString = modelInfo.ConnectionString; string invariantName = modelInfo.InvariantName; TreeNode attributesNode = new TreeNode("Connection Properties"); if (connectionString != null) { TreeNode cstringNode = new TreeNode("Connection String: " + connectionString); attributesNode.Nodes.Add(cstringNode); } if (invariantName != null) { TreeNode invNameNode = new TreeNode("Invariant Name: " + invariantName); attributesNode.Nodes.Add(invNameNode); } return attributesNode; } public TreeNode CreateMetaDataNode(string name) { TreeNode metaNode = new TreeNode("Db Objects"); metaNode.Name = name + ":MetaData"; DbModelInfo info = DbModelInfoService.GetDbModelInfo(name); DataTable metadataCollectionsTable = info.Tables[TableNames.MetaDataCollections]; if (metadataCollectionsTable != null) { for (int i = 0; i < TableNames.PrimaryObjects.Length; i++) { string metadataCollectionName = TableNames.PrimaryObjects[i]; LoggingService.Debug("looking for metadata: " + metadataCollectionName); DataTable metaCollectionTable = info.Tables[metadataCollectionName]; LoggingService.Debug("found metadata collection: " + metadataCollectionName); TreeNode collectionNode = new TreeNode(metadataCollectionName); collectionNode.Name = name + ":Collection:" + metadataCollectionName; metaNode.Nodes.Add(collectionNode); foreach (DataRow dbObjectRow in metaCollectionTable.Rows) { // TODO: >>>>>>>NEXT: at this point it is not enough to make this assumption - it doesn't hold // for SQLServer for example, only for Oracle. The following text illustrates the difference between // SQLServer and Oracle for the Users metadatacollection. /* * C:\workspace\ExampleDBFactories\DbExperiments\bin\Debug>DbExperiments.exe Users uid user_name createdate updatedate 0 public 08/04/2003 09:10:19 14/10/2005 01:36:15 1 dbo 08/04/2003 09:10:19 08/04/2003 09:10:19 2 guest 08/04/2003 09:10:19 08/04/2003 09:10:19 3 INFORMATION_SCHEMA 14/10/2005 01:36:06 14/10/2005 01:36:06 4 sys 14/10/2005 01:36:06 14/10/2005 01:36:06 5 ##MS_AgentSigningCertificate## 26/08/2006 23:04:16 26/08/2006 23:04:16 16384 db_owner 08/04/2003 09:10:19 14/10/2005 01:36:15 16385 db_accessadmin 08/04/2003 09:10:19 14/10/2005 01:36:15 16386 db_securityadmin 08/04/2003 09:10:19 14/10/2005 01:36:15 16387 db_ddladmin 08/04/2003 09:10:19 14/10/2005 01:36:15 16389 db_backupoperator 08/04/2003 09:10:19 14/10/2005 01:36:15 16390 db_datareader 08/04/2003 09:10:19 14/10/2005 01:36:15 16391 db_datawriter 08/04/2003 09:10:19 14/10/2005 01:36:15 16392 db_denydatareader 08/04/2003 09:10:19 14/10/2005 01:36:15 16393 db_denydatawriter 08/04/2003 09:10:19 14/10/2005 01:36:15 C:\workspace\ExampleDBFactories\DbExperiments\bin\Debug>DbExperiments.exe Users NAME ID CREATEDATE ANONYMOUS 28 07/02/2006 22:40:15 CTXSYS 25 07/02/2006 22:38:38 DBSNMP 23 07/02/2006 22:35:21 DEV 36 26/07/2006 00:09:58 DIP 18 07/02/2006 22:17:03 FLOWS_020100 35 07/02/2006 22:52:43 FLOWS_FILES 34 07/02/2006 22:52:43 HR 33 07/02/2006 22:51:21 MDSYS 32 07/02/2006 22:44:47 OUTLN 11 07/02/2006 22:10:24 SYS 0 07/02/2006 22:10:13 SYSTEM 5 07/02/2006 22:10:13 TSMSYS 20 07/02/2006 22:27:15 XDB 27 07/02/2006 22:40:14 C:\workspace\ExampleDBFactories\DbExperiments\bin\Debug> * * */ TreeNode objectNode = null; if (dbObjectRow.ItemArray.Length > 1) { objectNode = new TreeNode((string)dbObjectRow[1]); objectNode.Name = name + ":Object:" + (string)dbObjectRow[1]; } else { objectNode = new TreeNode((string)dbObjectRow[0]); objectNode.Name = name + ":Object:" + (string)dbObjectRow[0]; } // HACK All this building stuff needs to be externalise I think if (metadataCollectionName.Equals("Tables")) { // add the handler to invoke describer NodeAwareContextMenuStrip cMenu = new NodeAwareContextMenuStrip(objectNode); ToolStripMenuItem invokeDescriberMenuItem = new ToolStripMenuItem("Describe"); invokeDescriberMenuItem.Click += new EventHandler(DescribeTableClickHandler); cMenu.Items.Add(invokeDescriberMenuItem); objectNode.ContextMenuStrip = cMenu; } // TreeNode ownerNode = new TreeNode("Owner: " + (string)dbObjectRow["OWNER"]); // TreeNode typeNode = new TreeNode("Type: " + (string)dbObjectRow["TYPE"]); // // TODO: add fields to each Table // TreeNode fieldsNode = new TreeNode("Fields [TODO]"); // objectNode.Nodes.AddRange(new TreeNode[] {ownerNode, typeNode, fieldsNode }); collectionNode.Nodes.Add(objectNode); } } } return metaNode; } /// /// Uses a dialog to get the logical name of a new Connection then /// adds a new DbModelInfo for it to the cache and updates the DatabaseServer /// Tree. /// /// /// public void AddDbConnectionClickHandler(object sender, EventArgs e) { LoggingService.Debug("add connection clicked"); // get the logical name of the new connection string logicalName = null; using (GetConnectionLogicalNameDialog dialog = new GetConnectionLogicalNameDialog()) { dialog.ShowDialog(); logicalName = dialog.LogicalConnectionName; } if (logicalName.Equals("") || logicalName == null) return; LoggingService.Debug("name received is: " + logicalName); // add a new DbModelInfo to the cache DbModelInfoService.Add(logicalName, null, null); // rebuild the database server node this.BeginInvoke(new MethodInvoker(this.Rebuild)); } public void DeleteDbConnectionClickHandler(object sender, EventArgs e) { LoggingService.Debug("delete connection clicked"); } public void SaveDbModelInfoClickHandler(object sender, EventArgs e) { // save each DbModelInfo separately, confirming overwrite where necessary LoggingService.Debug("save all metadata clicked - will iterate through each and attempt to save"); IList names = DbModelInfoService.Names; foreach (string name in names) { bool saved = DbModelInfoService.SaveToFile(name, false); if (!saved) { DialogResult result = MessageBox.Show("Overwrite existing file for connection: " + name + "?", "File exists for connection", MessageBoxButtons.YesNo, MessageBoxIcon.Question, MessageBoxDefaultButton.Button1); if (result.Equals(DialogResult.Yes)) { DbModelInfoService.SaveToFile(name, true); } } } } public void LoadMetadataFromFileClickHandler(object sender, EventArgs e) { LoggingService.Debug("load metadata from file clicked"); string logicalConnectionName = getConnectionName(sender); DbModelInfoService.LoadFromFile(logicalConnectionName); this.BeginInvoke(new MethodInvoker(this.Rebuild)); } private static string getConnectionName(object sender) { ToolStripMenuItem menuItem = sender as ToolStripMenuItem; NodeAwareContextMenuStrip toolStrip = menuItem.Owner as NodeAwareContextMenuStrip; TreeNode node = toolStrip.TreeNode; while ((node.Tag == null) || (!node.Tag.Equals("ConnectionRoot"))) { node = node.Parent; } string connectionLogicalName = node.Text; return connectionLogicalName; } public void SetConnectionStringOnDbModelInfoClickHandler(object sender, EventArgs e) { string connectionLogicalName = getConnectionName(sender); LoggingService.Debug("add connection string clicked for item with name: " + connectionLogicalName); // use the ConnectionStringDefinitionDialog to get a connection string and invariant name ConnectionStringDefinitionDialog definitionDialog = new ConnectionStringDefinitionDialog(); DialogResult result = definitionDialog.ShowDialog(); // if the dialog was cancelled then do nothing if (result == DialogResult.Cancel) { return; } // if the dialog was submitted and connection string has changed then clear the DbModelInfo metadata // note that is is not required for the Connection string to be valid - it may be work // in progress and a user might want to save a partially formed connection string DbModelInfo dbModelInfo = DbModelInfoService.GetDbModelInfo(connectionLogicalName); string connectionString = dbModelInfo.ConnectionString; string newConnectionString = definitionDialog.ConnectionString; if (newConnectionString == null) { return; } dbModelInfo.ConnectionString = newConnectionString; dbModelInfo.InvariantName = definitionDialog.InvariantName; // rebuild the database explorer node this.BeginInvoke(new MethodInvoker(this.Rebuild)); } public void LoadMetadataFromConnectionClickHandler(object sender, EventArgs args) { string connectionLogicalName = getConnectionName(sender); LoggingService.Debug("load metadata from connection clicked for connection with name: " + connectionLogicalName); try { DbModelInfoService.LoadMetadataFromConnection(connectionLogicalName); } catch(DbException e) { MessageService.ShowError(e, "An Exception was thrown while trying to connect to: " + connectionLogicalName); } } public void DescribeTableClickHandler(object sender, EventArgs args) { string logicalConnectionName = getConnectionName(sender); ToolStripMenuItem item = sender as ToolStripMenuItem; NodeAwareContextMenuStrip s = item.Owner as NodeAwareContextMenuStrip; string tableName = s.TreeNode.Text; LoggingService.Debug("describe table clicked for: " + logicalConnectionName + " and table name: " + tableName); DataTable tableInfo = DbModelInfoService.GetTableInfo(logicalConnectionName, tableName); //DataSet dbModelInfo = DbModelInfoService.GetDbModelInfo(logicalConnectionName); // TableDescribeForm describeForm = new TableDescribeForm(tableInfo); // describeForm.Show(); TableDescribeViewContent tableDescribeViewContent = new TableDescribeViewContent(tableInfo, tableName); WorkbenchSingleton.Workbench.ShowView(tableDescribeViewContent); } } class NodeAwareContextMenuStrip : ContextMenuStrip { TreeNode treeNodeAttached; public NodeAwareContextMenuStrip(TreeNode treeNodeAttached) : base() { this.treeNodeAttached = treeNodeAttached; } public TreeNode TreeNode { get { return treeNodeAttached; } } } }