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






3.90/5 (6投票s)
一种极其快速高效的方法,可在 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
" 命令非常容易地实现。网上有很多文章描述了这一点,所以我在这里就不赘述了。
致谢和进一步信息
这篇文章可能看起来并不大,但它是巨大的头痛和在网上大海捞针(在针堆里找针)的最终结果。我找到了几篇有帮助的文章,其中一些是:
- http://www.thescripts.com/forum/thread255310.html - 从 C# 执行 Microsoft Access 模块
- http://support.microsoft.com/kb/306683 - 从 C# 执行 Microsoft Access 模块。请注意,这里描述的方法使用早期绑定,这意味着您将 COM 引用添加到您的项目,因此会使您的设置/安装包复杂化。
- https://codeproject.org.cn/cs/database/mdbcompact_latebind.asp - Alexander Yumashev 的一篇非常有用的文章,描述了如何在 C# 中压缩和修复 Microsoft Access 数据库。更重要的是,它通过使用后期绑定克服了上面提到的 Microsoft 文章中的问题。我用于动态链接到 Access 的代码来自这里。
- 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 日 - 修正了文章中的格式问题