65.9K
CodeProject 正在变化。 阅读更多。
Home

酷数据库工具

starIconstarIconstarIconstarIconemptyStarIcon

4.00/5 (2投票s)

2009年6月17日

CPOL

4分钟阅读

viewsIcon

18571

酷数据库工具。

在过去的两个月(2008年1月/2月),我在Codeproject赢得了C#月度文章奖,因此,我收到了很多免费软件。通常我从来不看这些软件。原因很简单。我写文章是为了与他人分享,所以这些免费软件对我来说没什么用。因为当我发表一篇新文章时,文章的读者不会安装与我相同的软件,因此无法在Visual Studio中运行我的代码。

因此,由于这个原因,我很少去看那些免费软件。

然而,在工作中,我经常与数据库打交道,以前都是SQL Server 2005。但现在是Oracle 10g。尽管我仍然更喜欢SQL Server。

所以因为我喜欢SQL Server,我一直在寻找可以帮助我日常工作的酷炫的东西。

Codeproject这两个月的奖品中,有一个产品实际上值得一看。因为我过去做过很多数据库相关的事情,所以我很欣赏任何能节省我时间的工具。我觉得这就是其中一款产品。

该产品是一个数据库重新同步组件。请注意,它不是一个应用程序,而是一个组件,这意味着您可以将其直接嵌入到您的应用程序中,并通过直接调用该组件上的UpdateDatabase()来使用它。

该组件名为“Database Restyle”,由一家名为Perpetuumsoft的公司开发,它可以直接集成到.NET项目中。

我认为展示组件功能的最好方法是看看它能做什么。我使用的是安装了标准Northwind数据库的SQL Server 2005。

37353/image-thumb7.png

现在跳转到Visual Studio(我使用的是VS2008),我们可以看到有一个实际的组件可以拖到Winforms/Console/Web应用程序中。

37353/image-thumb8.png

现在在代码中,我可以简单地使用组件的update方法,我的数据库将与当前模式上已更改的任何内容重新同步。这就是该应用程序使用的模型。

也许这里需要一个例子。我使用的是Database Restyle组件安装示例中附带的示例。

   1:  /*****************************************************************************
   2:  
   3:      This source file is a part of Database Restyle
   4:      
   5:      Copyright (c) 2008 Perpetuum Software LLC. All rights reserved.
   6:      THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY 
   7:      OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT
   8:      LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
   9:      FITNESS FOR A PARTICULAR PURPOSE.
  10:      
  11:      Copyright (c) 2008 Perpetuum Software LLC. All rights reserved.
  12:  
  13:  *****************************************************************************/
  14:  using System;
  15:  using System.Collections.Generic;
  16:  using System.ComponentModel;
  17:  using System.Data;
  18:  using System.Data.SqlClient;
  19:  using System.Data.Linq.Mapping;
  20:  using System.Drawing;
  21:  using System.Linq;
  22:  using System.Text;
  23:  using System.Windows.Forms;
  24:  using PerpetuumSoft.DataModel.MsSql.Synchronizers;
  25:  using PerpetuumSoft.DataModel.MsSql;
  26:  using PerpetuumSoft.DataModel.LinqToSql;
  27:   
  28:  namespace LinqToSql
  29:  {
  30:      public partial class MainForm : Form
  31:      {
  32:          public MainForm()
  33:          {
  34:              InitializeComponent();
  35:              dbBuilder.CreateFunctions = true;
  36:              dbBuilder.FunctionRequire += new 
  37:                  EventHandler<PerpetuumSoft.DataModel.FunctionRequareEventArgs>
  38:                  dbBuilder_FunctionRequire);
  39:          }
  40:   
  41:          private StringBuilder log;
  42:   
  43:          private LinqDatabaseBuilder dbBuilder = new LinqDatabaseBuilder();
  44:   
  45:          private void dbBuilder_FunctionRequire(object sender, 
  46:              PerpetuumSoft.DataModel.FunctionRequareEventArgs e)
  47:          {
  48:              if (e.SchemaName == "dbo" && e.FunctionName == "ProductsUnderThisUnitPrice")
  49:              {
  50:                  string text =
  51:                      @"CREATE FUNCTION [dbo].[ProductsUnderThisUnitPrice]()
  52:                      RETURNS int
  53:                      AS
  54:                      BEGIN
  55:                         DECLARE @retval int
  56:                         SELECT @retval = COUNT(*) FROM Territory
  57:                         RETURN @retval
  58:                      END;";
  59:                  ScalarFunction function = new 
  60:                      PerpetuumSoft.DataModel.MsSql.ScalarFunction(e.FunctionName,
                              text);
  61:                  function.ReturnValueType = new DataType.Int();
  62:                  e.Function = function;
  63:              }
  64:              else
  65:              {
  66:                  throw new Exception(String.Format("Unknown function: [{0}].[{1}].", 
  67:                      e.SchemaName, e.FunctionName));
  68:              }
  69:          }
  70:   
  71:          private void exitButton_Click(object sender, EventArgs e)
  72:          {
  73:              this.Close();
  74:          }
  75:   
  76:          private void syncButton_Click(object sender, EventArgs e)
  77:          {
  78:              try
  79:              {
  80:                  log = new StringBuilder();
  81:   
  82:                  MetaModel model = new AttributeMappingSource().
  83:                      GetModel(typeof(DataClassesDataContext));
  84:                  Database sourceDB = dbBuilder.CreateDatabase(model);
  85:                  databaseSync.UpdateDatabase(sourceDB, GetConnectionString());
  86:   
  87:                  logTextBox.Text = log.ToString();
  88:              }
  89:              catch (Exception ex)
  90:              {
  91:                  logTextBox.Text = log.ToString();
  92:                  logTextBox.Text += ex.ToString();
  93:              }
  94:          }
  95:   
  96:          private string GetConnectionString()
  97:          {
  98:              SqlConnectionStringBuilder connectionString = 
  99:                  new SqlConnectionStringBuilder();
 100:              connectionString.IntegratedSecurity = true;
 101:              connectionString.InitialCatalog = databaseName.Text;
 102:              connectionString.DataSource = serverName.Text;
 103:              return connectionString.ConnectionString;
 104:          }
 105:   
 106:          private void databaseSync_ScriptExecuting(object sender, 
 107:              PerpetuumSoft.DataModel.ScriptExecuteEventArgs e)
 108:          {
 109:              log.AppendLine(e.Text);
 110:          }
 111:   
 112:          private void databaseSync_DatabaseUpdating(object sender, 
 113:              PerpetuumSoft.DataModel.DatabaseUpdatingEventArgs e)
 114:          {
 115:              log.Append("Begin synchronize: [");
 116:              log.Append(DateTime.Now.ToLongTimeString());
 117:              log.AppendLine("]");
 118:          }
 119:   
 120:          private void databaseSync_DatabaseUpdated(object sender, EventArgs e)
 121:          {
 122:              log.Append("End synchronize: [");
 123:              log.Append(DateTime.Now.ToLongTimeString());
 124:              log.Append("]");
 125:          }
 126:   
 127:          private void clearDbButton_Click(object sender, EventArgs e)
 128:          {
 129:              try
 130:              {
 131:                  log = new StringBuilder();
 132:   
 133:                  Database sourceDB = Database.CreateDatabaseWithSystemObjects();
 134:                  databaseSync.UpdateDatabase(sourceDB, GetConnectionString());
 135:   
 136:                  logTextBox.Text = log.ToString();
 137:              }
 138:              catch (Exception ex)
 139:              {
 140:                  logTextBox.Text = log.ToString();
 141:                  logTextBox.Text += ex.ToString();
 142:              }
 143:          }
 144:   
 145:          private void viewScriptsButton_Click(object sender, EventArgs e)
 146:          {
 147:              try
 148:              {
 149:                  log = new StringBuilder();
 150:   
 151:                  MetaModel model = new AttributeMappingSource().
 152:                      GetModel(typeof(DataClassesDataContext));
 153:                  Database sourceDB = dbBuilder.CreateDatabase(model);
 154:                  DatabaseSynchronizer dbSynchronizer = 
 155:                      databaseSync.Compare(sourceDB, 
 156:                      databaseSync.ReverseDatabase(GetConnectionString()));
 157:                  foreach (Script script in dbSynchronizer.Scripts)
 158:                  {
 159:                      log.AppendLine(script.GetText());
 160:                  }
 161:                  logTextBox.Text = log.ToString();
 162:              }
 163:              catch (Exception ex)
 164:              {
 165:                  logTextBox.Text = log.ToString();
 166:                  logTextBox.Text += ex.ToString();
 167:              }
 168:          }
 169:   
 170:      }
 171:  }

上面最重要的几行是这些

   1:  MetaModel model = new AttributeMappingSource().
   2:     GetModel(typeof(DataClassesDataContext));
   3:  Database sourceDB = dbBuilder.CreateDatabase(model);
   4:  databaseSync.UpdateDatabase(sourceDB, GetConnectionString());

此示例使用LINQ to SQL文件作为数据库模式,该模式是运行时形成应用程序模型的模式。因此,这将是执行重新同步时与底层数据库进行比较的来源。可以看出,该模型(LINQ to SQL)存在3个表。这些表已经存在于底层的Northwind数据库中。

37353/image-thumb9.png

Here they are

37353/image-thumb10.png

但在与我之前展示的表单相关的代码中,创建了一个名为“ProductsUnderThisUnitPrice”的新Function,该Function不存在于底层的Northwind数据库中。

因此,运行应用程序会显示Database Restyle组件创建的这个新函数。

37353/image-thumb11.png

这很好,但是它实际上在底层数据库中创建了这个函数吗?

37353/image-thumb12.png

答案是可以。

因此,使用演示代码,我决定做一些激进的事情,清除数据库,然后进行重新同步。这也很有效。您可以在下面看到生成了一堆SQL来DROP表/约束等。

37353/image-thumb13.png

回到SQL,我们可以看到这些表不再位于Northwind数据库中。

37353/image-thumb14.png

然后我点击了“同步”按钮,回到SQL Server,一切又恢复了。这是该组件生成的用于重新创建架构的脚本。

   1:  Begin synchronize: [08:49:24]
   2:   
   3:  CREATE TABLE [dbo].[Categories]([CategoryID] INT NOT NULL IDENTITY(1,1),
   4:  [CategoryName] NVARCHAR(15) NOT NULL ,[Description] NTEXT NULL ,[Picture] IMAGE NULL )
   5:   
   6:  ALTER TABLE [dbo].[Categories] ADD CONSTRAINT [PK_Categories] PRIMARY KEY 
   7:  NONCLUSTERED ([CategoryID] ASC)  WITH(PAD_INDEX = OFF,IGNORE_DUP_KEY = OFF,
   8:  STATISTICS_NORECOMPUTE = OFF,ALLOW_ROW_LOCKS = ON,ALLOW_PAGE_LOCKS = ON)
   9:   
  10:  CREATE TABLE [dbo].[Products]([ProductID] INT NOT NULL IDENTITY(1,1),
  11:  [ProductName] NVARCHAR(40) NOT NULL ,[SupplierID] INT NULL ,[CategoryID] INT NULL ,
  12:  [QuantityPerUnit] NVARCHAR(20) NULL ,[UnitPrice] MONEY NULL ,[UnitsInStock] SMALLINT NULL ,
  13:  [UnitsOnOrder] SMALLINT NULL ,[ReorderLevel] SMALLINT NULL ,[Discontinued] BIT NOT NULL )
  14:   
  15:  ALTER TABLE [dbo].[Products] ADD CONSTRAINT [PK_Products] 
  16:  PRIMARY KEY NONCLUSTERED ([ProductID] ASC)  
  17:  WITH(PAD_INDEX = OFF,IGNORE_DUP_KEY = OFF,STATISTICS_NORECOMPUTE = OFF,
  18:  ALLOW_ROW_LOCKS = ON,ALLOW_PAGE_LOCKS = ON)
  19:   
  20:  CREATE TABLE [dbo].[Suppliers]([SupplierID] INT NOT NULL IDENTITY(1,1),
  21:  [CompanyName] NVARCHAR(40) NOT NULL ,[ContactName] NVARCHAR(30) NULL ,
  22:  [ContactTitle] NVARCHAR(30) NULL ,[Address] NVARCHAR(60) NULL ,[City] NVARCHAR(15) NULL ,
  23:  [Region] NVARCHAR(15) NULL ,[PostalCode] NVARCHAR(10) NULL ,[Country] NVARCHAR(15) NULL ,
  24:  [Phone] NVARCHAR(24) NULL ,[Fax] NVARCHAR(24) NULL ,[HomePage] NTEXT NULL )
  25:   
  26:  ALTER TABLE [dbo].[Suppliers] ADD CONSTRAINT [PK_Suppliers] 
  27:  PRIMARY KEY NONCLUSTERED ([SupplierID] ASC)  WITH(PAD_INDEX = OFF,IGNORE_DUP_KEY = OFF,
  28:  STATISTICS_NORECOMPUTE = OFF,ALLOW_ROW_LOCKS = ON,ALLOW_PAGE_LOCKS = ON)
  29:   
  30:  ALTER TABLE [dbo].[Products] WITH CHECK ADD CONSTRAINT [Category_Product] 
  31:  FOREIGN KEY (CategoryID) REFERENCES [dbo].[Categories] (CategoryID)  
  32:  ON UPDATE NO ACTION ON DELETE NO ACTION
  33:   
  34:  ALTER TABLE [dbo].[Products] WITH CHECK ADD CONSTRAINT [Supplier_Product] 
  35:  FOREIGN KEY (SupplierID) REFERENCES [dbo].[Suppliers] (SupplierID)  
  36:  ON UPDATE NO ACTION ON DELETE NO ACTION
  37:   
  38:  CREATE FUNCTION [dbo].[ProductsUnderThisUnitPrice]()
  39:                      RETURNS int
  40:                      AS
  41:                      BEGIN
  42:                         DECLARE @retval int
  43:                         SELECT @retval = COUNT(*) FROM Territory
  44:                         RETURN @retval
  45:                      END;
  46:  End synchronize: [08:49:24]

这是同步后的屏幕截图。

37353/image-thumb15.png

很酷,不是吗?

Perpetuumsoft还声称支持除使用LINQ to SQL之外的其他模式。

总而言之,我认为这是一个非常好的易于使用的产品。我认为这是一个非常有用(这就是我费心写博客的原因)的产品,并且每当我回到使用SQL Server时,我都将推动使用它。

如果您想下载并试用它,这是Perpetuumsoft的网站

© . All rights reserved.