You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
454 lines
16 KiB
454 lines
16 KiB
// <file> |
|
// <copyright see="prj:///doc/copyright.txt"/> |
|
// <license see="prj:///doc/license.txt"/> |
|
// <owner name="Dickon Field" email=""/> |
|
// <version>$Revision: 1766 $</version> |
|
// </file> |
|
|
|
/* |
|
* 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 |
|
{ |
|
/// <summary> |
|
/// 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. |
|
/// </summary> |
|
public class ServerBrowserTool : AbstractPadContent |
|
{ |
|
Panel ctl; |
|
|
|
/// <summary> |
|
/// ServerBrowserTool hosts one or more TreeViews providing views of types |
|
/// of server. Currently it shows only relational database servers. |
|
/// </summary> |
|
public ServerBrowserTool() |
|
{ |
|
LoggingService.Debug("Loading ServerBrowserTool"); |
|
ServerToolTreeView dbTree = new ServerToolTreeView(); |
|
dbTree.Dock = DockStyle.Fill; |
|
ctl = new Panel(); |
|
ctl.Controls.Add(dbTree); |
|
} |
|
|
|
/// <summary> |
|
/// The <see cref="System.Windows.Forms.Control"/> representing the pad |
|
/// </summary> |
|
public override Control Control { |
|
get { |
|
return ctl; |
|
} |
|
} |
|
|
|
/// <summary> |
|
/// Rebuildes the pad |
|
/// </summary> |
|
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. |
|
} |
|
|
|
/// <summary> |
|
/// Cleans up all used resources |
|
/// </summary> |
|
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(); |
|
|
|
} |
|
|
|
/// <summary> |
|
/// Rebuilds the database connection tree. |
|
/// Should only be called from a delegate via Invoke or BeginInvoke. |
|
/// </summary> |
|
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; |
|
} |
|
|
|
/// <summary> |
|
/// |
|
/// </summary> |
|
/// <param name="name"></param> |
|
/// <returns>a TreeNode representation of the connection properties |
|
/// of the connection</returns> |
|
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; |
|
} |
|
|
|
/// <summary> |
|
/// 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. |
|
/// </summary> |
|
/// <param name="sender"></param> |
|
/// <param name="e"></param> |
|
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<string> 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; |
|
} |
|
} |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|