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

在 C# 项目中,非常快速地将表从 ODBC 源复制到 Microsoft Access

starIconstarIconstarIcon
emptyStarIcon
starIcon
emptyStarIcon

3.90/5 (6投票s)

2007 年 11 月 27 日

CPOL

7分钟阅读

viewsIcon

135488

downloadIcon

948

一种极其快速高效的方法,可在 C# 项目中将任何支持 ODBC 的数据库中的表导入到 MS Access,方法是利用 Microsoft Access 内置的导入功能。

引言

在我最近的一个项目中,我遇到一个看似无解的严重问题。问题很简单:我需要将 SQL Server 数据库中的一个表中的大量数据复制到 Microsoft Access 数据库中的一个表中。然而,无论我采用哪种方法,复制的速度都太慢,完全无法接受。

我的参照点是 Microsoft Access 本身,它能够从 ODBC 源导入表。当我比较执行此导入所需的时间时,它始终以至少二十比一的巨大优势击败其他测试。

这听起来很大。我知道。我没有每个测试的确切时间,但它们都比通过 Microsoft Access 进行简单导入要长得多。我进行的比较测试是

  • 使用企业管理器
  • 使用 SQL Server Management Studio
  • 使用 SQL Server Information Services
  • 使用 BCP(尽管这只能导出到文本文件或 SQL Server 二进制文件)。BCP 进程本身就能与 Microsoft Access 导入的时间持平,但由于它无法导出到 Microsoft Access 而无用。

因此,最终的解决方案是找到一种方法,在 C# 项目中运行的同时让 Microsoft Access 执行复制。最大的问题是,在 .NET 项目中包含对 Microsoft Access 的支持自动意味着包含 COM,而且很多人一想到 COM 就会皱眉头。因此,本文讨论了 SQL Server 到 Microsoft Access 的复制过程的实现,该过程由 Microsoft Access 执行,在 C# 项目中运行,并通过使用后期绑定最大限度地减少 COM 对整个应用程序的影响。

在 .NET 中嵌入 COM 对象

虽然不那么美观,但这是可能的。它会使您的设置和安装更加复杂,但会增加 .NET Framework 本身可能无法提供的功能。底线是,是否要在项目中包含 COM 支持由您决定,您必须权衡利弊然后做出决定。

在我的情况下,别无选择。我可以保持我的项目整洁干净,让复制花费将近一个小时,或者找到一种优雅的方法来实现 COM 并将复制过程缩短到几分钟。选择很明确。

在您的项目中基本有两种方法可以使用 COM 对象

早期绑定

在这种情况下,您将要使用的 COM 对象添加到项目引用中,并利用 Visual Studio(在某些情况下)可以为该对象的类、方法和属性提供智能感知。对于屏幕控件,您还可以在屏幕设计器中将它们添加到窗体中。然而,缺点是,您不能再仅仅将项目文件从 `bin\debug` 或 `bin\release` 文件夹复制到另一台机器并运行程序。您现在需要一个设置项目,该项目将在目标计算机上复制并注册 COM 控件,以及复制您的项目文件。

延迟绑定

如果您确定要使用的 COM 对象已安装在目标计算机上,并且可以放弃智能感知等功能,那么您可以编写代码来连接到已安装的 COM 控件并以几乎相同的方式使用它。显而易见的缺点是,您不能保证代码总是有效(因为您不能 100% 确定 COM 对象已安装在目标计算机上),并且您将无法访问智能感知或屏幕设计器。

导入表

导入表到 Microsoft Access 时,该应用程序允许您通过转到“文件\外部数据\导入”来执行此操作。然后,您选择一个源,例如 ODBC 连接,选择数据库和表,即可完成。表将被复制(尽管没有键或索引,这些您之后需要重建)。

此功能通过 COM 自动化非常可用,因此可以在 C# 项目中使用。问题是代码仅在 Access 模块内部可用(尽管我相信读者中会有人找到直接从 C# 访问此函数的方法)。所以要访问它,您需要编写一个 VB 函数来导入表。然后,C# 项目需要加载 Access 数据库并执行该函数,该函数又执行导入。

此方法的唯一缺点是它不是异步的(尽管您可以在 C# 端添加线程),并且无法显示进度。

您需要在 Microsoft Access 中放入的 VB 函数非常简单,基本上是通过传递 DSN(指向源数据库)、源表名和目标表名来调用 `TransferDatabase` 方法。代码如下

Public Function Import(dsnName As String, sourceTableName As String, _
	targetTableName As String)
' if the table already exists, delete it.
   On Error GoTo CopyTable
   DoCmd.DeleteObject acTable, targetTableName
CopyTable:
   DoCmd.TransferDatabase _
      acImport, _
      "ODBC Database", _
      "ODBC;DSN=" + dsnName, _
      acTable, _
      sourceTableName, _
      targetTableName
End Function

然后是 C# 代码

object accessObject = null;
try
{
   accessObject = Activator.CreateInstance(Type.GetTypeFromProgID("Access.Application"));

   accessObject.GetType().InvokeMember(
      "OpenCurrentDatabase",
      System.Reflection.BindingFlags.Default | 
	System.Reflection.BindingFlags.InvokeMethod,
      null, 
      accessObject, 
      new Object[] { "AccessDbase.mdb" });

   accessObject.GetType().InvokeMember(
      "Run",
      System.Reflection.BindingFlags.Default | 
	System.Reflection.BindingFlags.InvokeMethod,
      null, 
      accessObject, 
      new Object[] { "Import", "DSN Name", "Source table name", "Target table name" });

   accessObject.GetType().InvokeMember(
      "CloseCurrentDatabase",
      System.Reflection.BindingFlags.Default | 
	System.Reflection.BindingFlags.InvokeMethod,
      null, 
      accessObject, 
      null);

   MessageBox.Show("Copy succeeded.");
}
catch (Exception ex)
{
   string message = ex.Message;
   while (ex.InnerException != null)
   {
      ex = ex.InnerException;
      message += "\r\n----\r\n" + ex.Message;
   }
   MessageBox.Show(message);
}
finally
{
   if (accessObject != null)
   {
      System.Runtime.InteropServices.Marshal.ReleaseComObject(accessObject);
      accessObject = null;
   }
}

我们在这里所做的就是创建一个 `Access.Application` 实例,打开一个数据库,执行 VB 模块以执行导入,关闭数据库并进行清理。

请注意,要使复制正常工作,您需要指定一些信息

  • Access 数据库的完全限定名
  • 在控制面板中创建的指向源数据库的 ODBC DSN 的名称(即,将从中复制表的表)
  • 表在源数据库中的名称
  • 您希望复制后分配给表的名称(可以与源名称相同)

最后,值得注意的是,复制过程创建的表没有键或索引,因此在复制完成后必须重建它们。然而,这可以通过 ADO 执行 SQL "ALTER TABLE " 命令非常容易地实现。网上有很多文章描述了这一点,所以我在这里就不赘述了。

致谢和进一步信息

这篇文章可能看起来并不大,但它是巨大的头痛和在网上大海捞针(在针堆里找针)的最终结果。我找到了几篇有帮助的文章,其中一些是:

  1. http://www.thescripts.com/forum/thread255310.html - 从 C# 执行 Microsoft Access 模块
  2. http://support.microsoft.com/kb/306683 - 从 C# 执行 Microsoft Access 模块。请注意,这里描述的方法使用早期绑定,这意味着您将 COM 引用添加到您的项目,因此会使您的设置/安装包复杂化。
  3. https://codeproject.org.cn/cs/database/mdbcompact_latebind.asp - Alexander Yumashev 的一篇非常有用的文章,描述了如何在 C# 中压缩和修复 Microsoft Access 数据库。更重要的是,它通过使用后期绑定克服了上面提到的 Microsoft 文章中的问题。我用于动态链接到 Access 的代码来自这里。
  4. https://codeproject.org.cn/cs/database/DSNAdmin.asp - 一篇关于如何动态创建 DSN 条目的好文章。通过将本文与此结合使用,您将不再需要依赖预先存在的 DSN,并且在复制完表后还可以通过删除 DSN 来清理。

结论

Microsoft Access 因许多原因而被淘汰,但它仍然被广泛使用。因此,如果您是那些仍然需要将大量数据从另一个数据库源复制到 Microsoft Access 的人之一,需要在 C# 项目中进行,并希望一种干净的方法来完成,那么我认为这篇文章会很有帮助。

如果您不符合此标准,那么我希望您至少读到了有趣的内容,并在未来能想到它。

历史

  • 2007 年 11 月 27 日 - 初稿
  • 2007 年 11 月 29 日 - 添加了关于动态 DSN 创建的文章引用
  • 2008 年 1 月 11 日 - 修正了文章中的格式问题
© . All rights reserved.